mbed port of tinydtls

dtls.c

Committer:
ashleymills
Date:
2013-10-11
Revision:
1:bc8a649bad13
Parent:
0:04990d454f45

File content as of revision 1:bc8a649bad13:

/* dtls -- a very basic DTLS implementation
 *
 * Copyright (C) 2011--2012 Olaf Bergmann <bergmann@tzi.org>
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#define __DEBUG__ 0

#ifndef __MODULE__
#define __MODULE__ "dtls.c"
#endif
#include "dbg.h" 

#include "config.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 "uthash.h"
#else /* WITH_CONTIKI */
# ifndef NDEBUG
#   define DEBUG DEBUG_PRINT
#   include "net/uip-debug.h"
#  endif /* NDEBUG */
#endif /* WITH_CONTIKI */

#include "debug.h"
#include "numeric.h"
#include "netq.h"
#include "dtls.h"

#ifdef WITH_SHA256
#  include "sha2/sha2.h"
#endif

//#include "bsd_socket.h"

#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)

#ifndef WITH_CONTIKI
#define HASH_FIND_PEER(head,sess,out)		\
  HASH_FIND(hh,head,sess,sizeof(session_t),out)
#define HASH_ADD_PEER(head,sess,add)		\
  HASH_ADD(hh,head,sess,sizeof(session_t),add)
#define HASH_DEL_PEER(head,delptr)		\
  HASH_DELETE(hh,head,delptr)
#endif /* WITH_CONTIKI */

#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_HV_LENGTH sizeof(dtls_hello_verify_t)
#define DTLS_SH_LENGTH (2 + 32 + 1 + 2 + 1)
#define DTLS_CKX_LENGTH 1
#define DTLS_FIN_LENGTH 12

#define HS_HDR_LENGTH  DTLS_RH_LENGTH + DTLS_HS_LENGTH
#define HV_HDR_LENGTH  HS_HDR_LENGTH + DTLS_HV_LENGTH

#define HIGH(V) (((V) >> 8) & 0xff)
#define LOW(V)  ((V) & 0xff)

#define DTLS_RECORD_HEADER(M) ((dtls_record_header_t *)(M))
#define DTLS_HANDSHAKE_HEADER(M) ((dtls_handshake_header_t *)(M))

#define HANDSHAKE(M) ((dtls_handshake_header_t *)((M) + DTLS_RH_LENGTH))
#define CLIENTHELLO(M) ((dtls_client_hello_t *)((M) + HS_HDR_LENGTH))

#define IS_HELLOVERIFY(M,L) \
      ((L) >= DTLS_HS_LENGTH + DTLS_HV_LENGTH && (M)[0] == DTLS_HT_HELLO_VERIFY_REQUEST)
#define IS_SERVERHELLO(M,L) \
      ((L) >= DTLS_HS_LENGTH + 6 && (M)[0] == DTLS_HT_SERVER_HELLO)
#define IS_SERVERHELLODONE(M,L) \
      ((L) >= DTLS_HS_LENGTH && (M)[0] == DTLS_HT_SERVER_HELLO_DONE)
#define IS_FINISHED(M,L) \
      ((L) >= DTLS_HS_LENGTH + DTLS_FIN_LENGTH && (M)[0] == DTLS_HT_FINISHED)

/* 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);				\
  }

uint8 _clear[DTLS_MAX_BUF]; /* target buffer message decryption */
uint8 _buf[DTLS_MAX_BUF]; /* target buffer for several crypto operations */

#ifndef NDEBUG
void hexdump(const unsigned char *packet, int length);
void dump(unsigned char *buf, size_t len);
#endif

/* 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";

extern void netq_init();
extern void crypto_init();
extern void peer_init();

dtls_context_t the_dtls_context;

void
dtls_init() {
  dtls_clock_init();
  netq_init();
  crypto_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)

/** 
 * 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.
 */
int dtls_send(dtls_context_t *ctx, dtls_peer_t *peer, unsigned char type,
	      uint8 *buf, size_t buflen);

/**
 * Stops ongoing retransmissions of handshake messages for @p peer.
 */
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) {
  DBG("dtls_get_peer");
  dtls_peer_t *p = NULL;
  DBG("trying to get hash for the following byte sequence: ");
  #if __DEBUG__ > 0
  for(uint8_t i=0; i<sizeof(session_t); i++) {
     DBGX("%x ",((uint8_t*)session)[i]);
  }
  #endif
  DBGX("\r\n");
  DBG("session size: %u, AF: %u, address: %s:%d, ifnumber: %d",
     session->size,
     session->addr.sin.sin_family,
     inet_ntoa(session->addr.sin.sin_addr),
     ntohs(session->addr.sin.sin_port),
     session->ifindex
  );
  
#ifndef WITH_CONTIKI
  HASH_FIND_PEER(ctx->peers, session, p);
#else /* WITH_CONTIKI */
  for (p = list_head(ctx->peers); p; p = list_item_next(p))
    if (dtls_session_equals(&p->session, session))
      return p;
#endif /* WITH_CONTIKI */
  DBG("hash returned pointer to peer @ %u",p);
  return p;
}

void
dtls_add_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
  DBG("dtls_add_peer");
  DBG("Trying to add peer @ %u with session byte sequence:",peer);
  #if __DEBUG__ > 0
  for(uint8_t i=0; i<sizeof(session_t); i++) {
     DBGX("%x ",((uint8_t*)(&peer->session))[i]);
  }
  #endif
  DBGX("\r\n");
  DBG("session size: %u, address: %s:%d, ifnumber: %d",
     peer->session.size,
     inet_ntoa(peer->session.addr.sin.sin_addr),
     ntohs(peer->session.addr.sin.sin_port),
     peer->session.ifindex
  );
  //#define HASH_ADD_PEER(head,sess,add)		\
  //HASH_ADD(hh,head,sess,sizeof(session_t),add)
#ifndef WITH_CONTIKI
  HASH_ADD_PEER(ctx->peers, session, peer);
#else // WITH_CONTIKI
  list_add(ctx->peers, peer);
#endif // WITH_CONTIKI
}

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);
    }
  }
}

int
dtls_get_cookie(uint8 *msg, int 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)
      || dtls_uint16_to_int(msg + DTLS_HS_LENGTH) != DTLS_VERSION)
    return -1;
  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 -1;
  
  *cookie = msg + sizeof(uint8);
  return dtls_uint8_to_int(msg);

 error:
  return -1;
}

int
dtls_create_cookie(dtls_context_t *ctx, 
		   session_t *session,
		   uint8 *msg, int msglen,
		   uint8 *cookie, int *clen) {
  unsigned char buf[DTLS_HMAC_MAX];
  size_t len, e;

  /* 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);

  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);

  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 1;
}

#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, int 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 peer.
 * \return pointer to the next byte after the written header
 */ 
static inline uint8 *
dtls_set_record_header(uint8 type, dtls_peer_t *peer, uint8 *buf) {
  
  dtls_int_to_uint8(buf, type);
  buf += sizeof(uint8);

  dtls_int_to_uint16(buf, DTLS_VERSION);
  buf += sizeof(uint16);

  if (peer) {
    memcpy(buf, &peer->epoch, sizeof(uint16) + sizeof(uint48));

    /* increment record sequence counter by 1 */
    inc_uint(uint48, peer->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) {
    /* increment handshake message sequence counter by 1 */
    inc_uint(uint16, peer->hs_state.mseq);
  
    /* and copy the result to buf */
    memcpy(buf, &peer->hs_state.mseq, sizeof(uint16));
  } 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;
}
  
/**
 * 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 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.
 */
int
dtls_verify_peer(dtls_context_t *ctx, 
		 dtls_peer_t *peer, 
		 session_t *session,
		 uint8 *record, 
		 uint8 *data, size_t data_length) {
  DBG("Entering dtls_verify_peer");
  int len = DTLS_COOKIE_LENGTH;
  uint8 *cookie, *p;
#undef mycookie
#define mycookie (ctx->sendbuf + HV_HDR_LENGTH)

  /* check if we can access at least all fields from the handshake header */
  DBG("record[0]: %d, (%d) data_length: %d (%d), data[0]: %d (%d)\r\n",
     record[0],DTLS_CT_HANDSHAKE,data_length,DTLS_HS_LENGTH,data[0],DTLS_HT_CLIENT_HELLO);
  
  if (record[0] == DTLS_CT_HANDSHAKE
      && data_length >= DTLS_HS_LENGTH 
      && data[0] == DTLS_HT_CLIENT_HELLO) {

    /* Store cookie where we can reuse it for the HelloVerify request. */
    if (dtls_create_cookie(ctx, session, data, data_length,mycookie, &len) < 0) {
      DBG("Cannot create cookie");
      return -1;
    }
/* #ifndef NDEBUG */
/*     DBG("create cookie: "); */
/*     dump(mycookie, len); */
/*     printf("\n"); */
/* #endif */
    assert(len == DTLS_COOKIE_LENGTH);
    
    /* Perform cookie check. */
    len = dtls_get_cookie(data, data_length, &cookie);

/* #ifndef NDEBUG */
/*     DBG("compare with cookie: "); */
/*     dump(cookie, len); */
/*     printf("\n"); */
/* #endif */

    /* check if cookies match */
    if (len == DTLS_COOKIE_LENGTH && memcmp(cookie, mycookie, len) == 0) {
      DBG("Found matching cookie");
      return 1;      
    }
    if (len > 0) {
      DBG("invalid cookie");
#ifndef NDEBUG
      dump(cookie, len);
      DBGX("\r\n");
#endif
    }
    /* ClientHello did not contain any valid cookie, hence we send a
     * HelloVerify request. */

    p = dtls_set_handshake_header(DTLS_HT_HELLO_VERIFY_REQUEST,
				  peer, DTLS_HV_LENGTH + DTLS_COOKIE_LENGTH,
				  0, DTLS_HV_LENGTH + DTLS_COOKIE_LENGTH, 
				  ctx->sendbuf + DTLS_RH_LENGTH);

    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;

    if (!peer) {
      /* It's an initial ClientHello, so we set the record header
       * manually and send the HelloVerify request using the
       * registered write callback. */

      dtls_set_record_header(DTLS_CT_HANDSHAKE, NULL, ctx->sendbuf);
      /* set packet length */
      dtls_int_to_uint16(ctx->sendbuf + 11, 
			 p - (ctx->sendbuf + DTLS_RH_LENGTH));

      (void)CALL(ctx, write, session, ctx->sendbuf, p - ctx->sendbuf);
    } else {
      if (peer->epoch) {
	      DBG("renegotiation, therefore we accept it anyway:");
	      return 1;
      }

      if (dtls_send(ctx, peer, DTLS_CT_HANDSHAKE, 
		    ctx->sendbuf + DTLS_RH_LENGTH, 
		    p - (ctx->sendbuf + DTLS_RH_LENGTH)) < 0) {
	      WARN("Cannot send HelloVerify request");
	      return -1;
      }
    }

    return 0; /* HelloVerify is sent, now we cannot do anything but wait */
  }
	DBG("Not a ClientHello, signal error");
  return -1;			/* not a ClientHello, signal error */
#undef mycookie
}

/** only one compression method is currently defined */
uint8 compression_methods[] = { 
  TLS_COMP_NULL 
};

/**
 * Returns @c 1 if @p code is a cipher suite other than @c
 * TLS_NULL_WITH_NULL_NULL that we recognize.
 *
 * @param code The cipher suite identifier to check
 * @return @c 1 iff @p code is recognized,
 */ 
static inline int
known_cipher(dtls_cipher_t code) {
  return code == TLS_PSK_WITH_AES_128_CCM_8;
}

int
calculate_key_block(dtls_context_t *ctx, 
		    dtls_security_parameters_t *config,
		    const dtls_key_t *key,
		    unsigned char client_random[32],
		    unsigned char server_random[32]) {
  unsigned char *pre_master_secret;
  size_t pre_master_len = 0;
  pre_master_secret = config->key_block;

  assert(key);
  switch (key->type) {
  case DTLS_KEY_PSK: {
  /* Temporarily use the key_block storage space for the pre master secret. */
    pre_master_len = dtls_pre_master_secret(key->key.psk.key, key->key.psk.key_length, 
					  pre_master_secret);
    
    break;
  }
  default:
    DBG("Calculate_key_block: unknown key type");
    return 0;
  }

/* #ifndef NDEBUG */
/*   { */
/*     int i; */

/*     printf("client_random:"); */
/*     for (i = 0; i < 32; ++i) */
/*       printf(" %02x", client_random[i]); */
/*     printf("\n"); */

/*     printf("server_random:"); */
/*     for (i = 0; i < 32; ++i) */
/*       printf(" %02x", server_random[i]); */
/*     printf("\n"); */

/*     printf("psk: (%lu bytes):", key->key.psk.key_length); */
/*     hexdump(key->key.psk.key, key->key.psk.key_length); */
/*     printf("\n"); */

/*     printf("pre_master_secret: (%lu bytes):", pre_master_len); */
/*     for (i = 0; i < pre_master_len; ++i) */
/*       printf(" %02x", pre_master_secret[i]); */
/*     printf("\n"); */
/*   } */
/* #endif /\* NDEBUG *\/ */

  dtls_prf(pre_master_secret, pre_master_len,
	   PRF_LABEL(master), PRF_LABEL_SIZE(master),
	   client_random, 32,
	   server_random, 32,
	   config->master_secret, 
	   DTLS_MASTER_SECRET_LENGTH);

/* #ifndef NDEBUG */
/*   { */
/*     int i; */
/*     printf("master_secret (%d bytes):", DTLS_MASTER_SECRET_LENGTH); */
/*     for (i = 0; i < DTLS_MASTER_SECRET_LENGTH; ++i) */
/*       printf(" %02x", config->master_secret[i]); */
/*     printf("\n"); */
/*   } */
/* #endif /\* NDEBUG *\/ */

  /* create key_block from master_secret
   * key_block = PRF(master_secret,
                    "key expansion" + server_random + client_random) */

  dtls_prf(config->master_secret, 
	   DTLS_MASTER_SECRET_LENGTH,
	   PRF_LABEL(key), PRF_LABEL_SIZE(key),
	   server_random, 32,
	   client_random, 32,
	   config->key_block,
	   dtls_kb_size(config));

/* #ifndef NDEBUG */
/*   { */
/*       printf("key_block (%d bytes):\n", dtls_kb_size(config)); */
/*       printf("  client_MAC_secret:\t");   */
/*       dump(dtls_kb_client_mac_secret(config),  */
/* 	   dtls_kb_mac_secret_size(config)); */
/*       printf("\n"); */

/*       printf("  server_MAC_secret:\t");   */
/*       dump(dtls_kb_server_mac_secret(config),  */
/* 	   dtls_kb_mac_secret_size(config)); */
/*       printf("\n"); */

/*       printf("  client_write_key:\t");   */
/*       dump(dtls_kb_client_write_key(config),  */
/* 	   dtls_kb_key_size(config)); */
/*       printf("\n"); */

/*       printf("  server_write_key:\t");   */
/*       dump(dtls_kb_server_write_key(config),  */
/* 	   dtls_kb_key_size(config)); */
/*       printf("\n"); */

/*       printf("  client_IV:\t\t");   */
/*       dump(dtls_kb_client_iv(config),  */
/* 	   dtls_kb_iv_size(config)); */
/*       printf("\n"); */
      
/*       printf("  server_IV:\t\t");   */
/*       dump(dtls_kb_server_iv(config),  */
/* 	   dtls_kb_iv_size(config)); */
/*       printf("\n"); */
      

/*   } */
/* #endif */
  return 1;
}

/**
 * Updates the security parameters of given \p peer.  As this must be
 * done before the new configuration is activated, it changes the
 * OTHER_CONFIG only. When the ClientHello handshake message in \p
 * data does not contain a cipher suite or compression method, it is 
 * copied from the CURRENT_CONFIG.
 *
 * \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 0 if an error occurred, \c 1 otherwise.
 */
int
dtls_update_parameters(dtls_context_t *ctx, 
		       dtls_peer_t *peer,
		       uint8 *data, size_t data_length) {
  int i, j;
  int ok;
  dtls_security_parameters_t *config = OTHER_CONFIG(peer);

  assert(config);
  assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH);

  /* DBG("dtls_update_parameters: msglen is %d\n", data_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 
   * FIXME: if we send the ServerHello here, we do not need to store
   * the client's random bytes */
  memcpy(config->client_random, data, sizeof(config->client_random));
  data += sizeof(config->client_random);
  data_length -= sizeof(config->client_random);

  /* 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 (CURRENT_CONFIG(peer)->cipher == TLS_NULL_WITH_NULL_NULL)
      goto error;

    config->cipher = CURRENT_CONFIG(peer)->cipher;
    config->compression = CURRENT_CONFIG(peer)->compression;

    return 1;
  }

  data += sizeof(uint16);
  data_length -= sizeof(uint16) + i;

  ok = 0;
  while (i && !ok) {
    config->cipher = dtls_uint16_to_int(data);
    ok = known_cipher(config->cipher);
    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;
    return 0;
  }

  if (data_length < sizeof(uint8)) { 
    /* no compression specified, take the current compression method */
    config->compression = CURRENT_CONFIG(peer)->compression;
    return 1;
  }

  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);    
  }
  
  return ok;
 error:
  WARN("ClientHello too short (%d bytes)", data_length);
  return 0;
}

static inline int
check_client_keyexchange(dtls_context_t *ctx, 
			 dtls_peer_t *peer,
			 uint8 *data, size_t length) {
  return length >= DTLS_CKX_LENGTH && data[0] == DTLS_HT_CLIENT_KEY_EXCHANGE;
}

static int
check_ccs(dtls_context_t *ctx, 
	  dtls_peer_t *peer,
	  uint8 *record, uint8 *data, size_t data_length) {

  if (DTLS_RECORD_HEADER(record)->content_type != DTLS_CT_CHANGE_CIPHER_SPEC
      || data_length < 1 || data[0] != 1)
    return 0;

  /* set crypto context for TLS_PSK_WITH_AES_128_CCM_8 */
  /* client */
  dtls_cipher_free(OTHER_CONFIG(peer)->read_cipher);

  assert(OTHER_CONFIG(peer)->cipher != TLS_NULL_WITH_NULL_NULL);
  OTHER_CONFIG(peer)->read_cipher = 
    dtls_cipher_new(OTHER_CONFIG(peer)->cipher,
		    dtls_kb_client_write_key(OTHER_CONFIG(peer)),
		    dtls_kb_key_size(OTHER_CONFIG(peer)));

  if (!OTHER_CONFIG(peer)->read_cipher) {
    WARN("Cannot create read cipher");
    return 0;
  }

  dtls_cipher_set_iv(OTHER_CONFIG(peer)->read_cipher,
		     dtls_kb_client_iv(OTHER_CONFIG(peer)),
		     dtls_kb_iv_size(OTHER_CONFIG(peer)));

  /* server */
  dtls_cipher_free(OTHER_CONFIG(peer)->write_cipher);
  
  OTHER_CONFIG(peer)->write_cipher = 
    dtls_cipher_new(OTHER_CONFIG(peer)->cipher,
		    dtls_kb_server_write_key(OTHER_CONFIG(peer)),
		    dtls_kb_key_size(OTHER_CONFIG(peer)));

  if (!OTHER_CONFIG(peer)->write_cipher) {
    WARN("Cannot create write cipher");
    return 0;
  }

  dtls_cipher_set_iv(OTHER_CONFIG(peer)->write_cipher,
		     dtls_kb_server_iv(OTHER_CONFIG(peer)),
		     dtls_kb_iv_size(OTHER_CONFIG(peer)));

  return 1;
}

#ifndef NDEBUG
extern size_t dsrv_print_addr(const session_t *, unsigned char *, size_t);
#endif

static inline void
update_hs_hash(dtls_peer_t *peer, uint8 *data, size_t length) {
/* #ifndef NDEBUG */
/*   printf("add MAC data: "); */
/*   dump(data, length); */
/*   printf("\n"); */
/* #endif */
  dtls_hash_update(&peer->hs_state.hs_hash, data, length);
}

static inline size_t
finalize_hs_hash(dtls_peer_t *peer, uint8 *buf) {
  return dtls_hash_finalize(buf, &peer->hs_state.hs_hash);
}

static inline void
clear_hs_hash(dtls_peer_t *peer) {
  assert(peer);
  dtls_hash_init(&peer->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 record The message record header.
 * \param rlen   The actual length of \p record.
 * \param data   The cleartext payload of the message.
 * \param data_length Actual length of \p data.
 * \return \c 1 if the Finished message is valid, \c 0 otherwise.
 */
static int
check_finished(dtls_context_t *ctx, dtls_peer_t *peer,
	       uint8 *record, uint8 *data, size_t data_length) {
  size_t digest_length, label_size;
  const unsigned char *label;
  unsigned char buf[DTLS_HMAC_MAX];

  /* 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;

  DBG("Check Finish message");
  if(record[0] != DTLS_CT_HANDSHAKE || !IS_FINISHED(data, data_length)) {
    DBG("Failed");
    return 0;
  }

  /* temporarily store hash status for roll-back after finalize */
  memcpy(b.statebuf, &peer->hs_state.hs_hash, DTLS_HASH_CTX_SIZE);

  digest_length = finalize_hs_hash(peer, buf);
  /* clear_hash(); */

  /* restore hash status */
  memcpy(&peer->hs_state.hs_hash, b.statebuf, DTLS_HASH_CTX_SIZE);

  if (CURRENT_CONFIG(peer)->role == DTLS_SERVER) {
    label = PRF_LABEL(server);
    label_size = PRF_LABEL_SIZE(server);
  } else { /* client */
    label = PRF_LABEL(client);
    label_size = PRF_LABEL_SIZE(client);
  }

  dtls_prf(CURRENT_CONFIG(peer)->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));
  
/* #ifndef NDEBUG */
/*   printf("d:\t"); dump(data + DTLS_HS_LENGTH, sizeof(b.verify_data)); printf("\n"); */
/*   printf("v:\t"); dump(b.verify_data, sizeof(b.verify_data)); printf("\n"); */
/* #endif */
  return 
    memcmp(data + DTLS_HS_LENGTH, b.verify_data, sizeof(b.verify_data)) == 0;
}

/**
 * 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 type    The content type of this record.
 * \param data    The payload to send.
 * \param data_length The size of \p data.
 * \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.
 */
int
dtls_prepare_record(dtls_peer_t *peer,
		    unsigned char type,
		    uint8 *data, size_t data_length,
		    uint8 *sendbuf, size_t *rlen) {
  uint8 *p;
  int res;
  
  /* check the minimum that we need for packets that are not encrypted */
  if (*rlen < DTLS_RH_LENGTH + data_length) {
    DBG("dtls_prepare_record: send buffer too small");
    return -1;
  }

  p = dtls_set_record_header(type, peer, sendbuf);

  if (CURRENT_CONFIG(peer)->cipher == TLS_NULL_WITH_NULL_NULL) {
    /* no cipher suite */
    memcpy(p, data, data_length);
    res = data_length;
  } else { /* TLS_PSK_WITH_AES_128_CCM_8 */   
    dtls_cipher_context_t *cipher_context;

    /** 
     * 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
#define A_DATA N
    unsigned char N[max(DTLS_CCM_BLOCKSIZE, A_DATA_LEN)];
    
    if (*rlen < sizeof(dtls_record_header_t) + data_length + 8) {
      WARN("dtls_prepare_record(): send buffer too small");
      return -1;
    }

    DBG("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_8");

    /* set nonce       
       from http://tools.ietf.org/html/draft-mcgrew-tls-aes-ccm-03:
        struct {
               case client:
                  uint32 client_write_IV;  // low order 32-bits
               case server:
                  uint32 server_write_IV;  // low order 32-bits
               uint64 seq_num;
            } CCMNonce.

	    In DTLS, the 64-bit seq_num is the 16-bit epoch concatenated with the
	    48-bit seq_num.
    */

    memcpy(p, &DTLS_RECORD_HEADER(sendbuf)->epoch, 8);
    memcpy(p + 8, data, data_length);

    memset(N, 0, DTLS_CCM_BLOCKSIZE);
    memcpy(N, dtls_kb_local_iv(CURRENT_CONFIG(peer)), 
	   dtls_kb_iv_size(CURRENT_CONFIG(peer)));
    memcpy(N + dtls_kb_iv_size(CURRENT_CONFIG(peer)), p, 8); /* epoch + seq_num */

    cipher_context = CURRENT_CONFIG(peer)->write_cipher;

    if (!cipher_context) {
      WARN("no write_cipher available!");
      return -1;
    }
/* #ifndef NDEBUG */
/*     printf("nonce:\t"); */
/*     dump(N, DTLS_CCM_BLOCKSIZE); */
/*     printf("\nkey:\t"); */
/*     dump(dtls_kb_local_write_key(CURRENT_CONFIG(peer)),  */
/* 	 dtls_kb_key_size(CURRENT_CONFIG(peer))); */
/*     printf("\n"); */
/* #endif */
    dtls_cipher_set_iv(cipher_context, N, DTLS_CCM_BLOCKSIZE);
    
    /* 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, data_length); /* length */
    
    res = dtls_encrypt(cipher_context, p + 8, data_length, p + 8,
		       A_DATA, A_DATA_LEN);

    if (res < 0)
      return -1;

/* #ifndef NDEBUG */
/*     dump(p, res + 8); */
/*     printf("\n"); */
/* #endif */
    res += 8;			/* increment res by size of nonce_explicit */
  }

  /* fix length of fragment in sendbuf */
  dtls_int_to_uint16(sendbuf + 11, res);
  
  *rlen = DTLS_RH_LENGTH + res;
  return 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.
 */
int
dtls_send(dtls_context_t *ctx, dtls_peer_t *peer,
	  unsigned char type,
	  uint8 *buf, size_t buflen) {
  
  /* 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;

  res = dtls_prepare_record(peer, type, buf, buflen, sendbuf, &len);

  if (res < 0)
    return res;

  /* if (peer && MUST_HASH(peer, type, buf, buflen)) */
  /*   update_hs_hash(peer, buf, buflen); */
  
/* #ifndef NDEBUG */
/*   DBG("send %d bytes\n", buflen); */
/*   hexdump(sendbuf, sizeof(dtls_record_header_t)); */
/*   printf("\n"); */
/*   hexdump(buf, buflen); */
/*   printf("\n"); */
/* #endif */

  if (type == DTLS_CT_HANDSHAKE && buf[0] != DTLS_HT_HELLO_VERIFY_REQUEST) {
    /* copy handshake messages other than HelloVerify into retransmit buffer */
    netq_t *n = netq_node_new();
    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->length = buflen;
      memcpy(n->data, buf, buflen);

      if (!netq_insert_node((netq_t **)ctx->sendqueue, n)) {
	WARN("Cannot add packet to retransmit buffer");
	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 */
	DBG("Copied to sendqueue");
#endif /* WITH_CONTIKI */
      }
    } else 
      WARN("Retransmit buffer full");
  }

  /* FIXME: copy to peer's sendqueue (after fragmentation if
   * necessary) and initialize retransmit timer */
  res = CALL(ctx, write, &peer->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 : buflen - (len - res);
}

static inline int
dtls_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_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE);
    /* indicate tear down */
    peer->state = DTLS_STATE_CLOSING;
  }
  return res;
}

int
dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) {

  static uint8 buf[DTLS_MAX_BUF];
  uint8 *p = buf, *q = ctx->sendbuf;
  size_t qlen = sizeof(ctx->sendbuf);
  int res;
  const dtls_key_t *key;
  dtls_tick_t now;

  /* 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.) */
  assert(sizeof(buf) >=
	 DTLS_RH_LENGTH + DTLS_HS_LENGTH + DTLS_SH_LENGTH + 20);

  if (CALL(ctx, get_key, &peer->session, NULL, 0, &key) < 0) {
    DBG("dtls_send_server_hello(): no key for session available");
    return -1;
  }

  /* Handshake header */
  p = dtls_set_handshake_header(DTLS_HT_SERVER_HELLO, 
				peer,
				DTLS_SH_LENGTH, 
				0, DTLS_SH_LENGTH,
				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(p, now / CLOCK_SECOND);
  prng(p + 4, 28);

  if (!calculate_key_block(ctx, OTHER_CONFIG(peer), key, 
			   OTHER_CONFIG(peer)->client_random, p))
    return -1;

  p += 32;

  *p++ = 0;			/* no session id */

  if (OTHER_CONFIG(peer)->cipher != TLS_NULL_WITH_NULL_NULL) {
    /* selected cipher suite */
    dtls_int_to_uint16(p, OTHER_CONFIG(peer)->cipher);
    p += sizeof(uint16);

    /* selected compression method */
    if (OTHER_CONFIG(peer)->compression >= 0)
      *p++ = compression_methods[OTHER_CONFIG(peer)->compression];

    /* FIXME: if key->psk.id != NULL we need the server key exchange */

    /* update the finish hash 
       (FIXME: better put this in generic record_send function) */
    update_hs_hash(peer, buf, p - buf);
  }

  res = dtls_prepare_record(peer, DTLS_CT_HANDSHAKE, 
			    buf, p - buf,
			    q, &qlen);
  if (res < 0) {
    DBG("dtls_server_hello: cannot prepare ServerHello record");
    return res;
  }

  q += qlen;
  qlen = sizeof(ctx->sendbuf) - qlen;

  /* ServerHelloDone 
   *
   * Start message construction at beginning of buffer. */
  p = dtls_set_handshake_header(DTLS_HT_SERVER_HELLO_DONE, 
				peer,
				0, /* ServerHelloDone has no extra fields */
				0, 0, /* ServerHelloDone has no extra fields */
				buf);

  /* update the finish hash 
     (FIXME: better put this in generic record_send function) */
  update_hs_hash(peer, buf, p - buf);

  res = dtls_prepare_record(peer, DTLS_CT_HANDSHAKE, 
			    buf, p - buf,
			    q, &qlen);
  if (res < 0) {
    DBG("dtls_server_hello: cannot prepare ServerHelloDone record");
    return res;
  }

  return CALL(ctx, write, &peer->session,  
		  ctx->sendbuf, (q + qlen) - ctx->sendbuf);
}

static inline int 
dtls_send_ccs(dtls_context_t *ctx, dtls_peer_t *peer) {
  ctx->sendbuf[0] = 1;
  return dtls_send(ctx, peer, DTLS_CT_CHANGE_CIPHER_SPEC, ctx->sendbuf, 1);
}

    
int 
dtls_send_kx(dtls_context_t *ctx, dtls_peer_t *peer, int is_client) {
  const dtls_key_t *key;
  uint8 *p = ctx->sendbuf;
  size_t size;
  int ht = is_client 
    ? DTLS_HT_CLIENT_KEY_EXCHANGE 
    : DTLS_HT_SERVER_KEY_EXCHANGE;
  unsigned char *id = NULL;
  size_t id_len = 0;

  if (CALL(ctx, get_key, &peer->session, NULL, 0, &key) < 0) {
    DBG("No key to send in kx. Fail.");
    return -2;
  }

  assert(key);

  switch (key->type) {
  case DTLS_KEY_PSK: {
    id_len = key->key.psk.id_length;
    id = key->key.psk.id;
    break;
  }
  default:
    DBG("Key type not supported. Fail.");
    return -3;
  }
  
  size = id_len + sizeof(uint16);
  p = dtls_set_handshake_header(ht, peer, size, 0, size, p);

  dtls_int_to_uint16(p, id_len);
  memcpy(p + sizeof(uint16), id, id_len);

  p += size;

  update_hs_hash(peer, ctx->sendbuf, p - ctx->sendbuf);
  return dtls_send(ctx, peer, DTLS_CT_HANDSHAKE, 
		   ctx->sendbuf, p - ctx->sendbuf);
}

#define msg_overhead(Peer,Length) (DTLS_RH_LENGTH +	\
  ((Length + dtls_kb_iv_size(CURRENT_CONFIG(Peer)) + \
    dtls_kb_digest_size(CURRENT_CONFIG(Peer))) /     \
   DTLS_BLK_LENGTH + 1) * DTLS_BLK_LENGTH)

int
dtls_send_server_finished(dtls_context_t *ctx, dtls_peer_t *peer) {

  int length;
  uint8 buf[DTLS_HMAC_MAX];
  uint8 *p = ctx->sendbuf;

  /* FIXME: adjust message overhead calculation */
  assert(msg_overhead(peer, DTLS_HS_LENGTH + DTLS_FIN_LENGTH) 
	 < sizeof(ctx->sendbuf));

  p = dtls_set_handshake_header(DTLS_HT_FINISHED, 
                                peer, DTLS_FIN_LENGTH, 0, DTLS_FIN_LENGTH, p);
  
  length = finalize_hs_hash(peer, buf);

  dtls_prf(CURRENT_CONFIG(peer)->master_secret, 
	   DTLS_MASTER_SECRET_LENGTH,
	   PRF_LABEL(server), PRF_LABEL_SIZE(server), 
	   PRF_LABEL(finished), PRF_LABEL_SIZE(finished), 
	   buf, length,
	   p, DTLS_FIN_LENGTH);

/* #ifndef NDEBUG */
/*   printf("server finished MAC:\t"); */
/*   dump(p, DTLS_FIN_LENGTH); */
/*   printf("\n"); */
/* #endif */

  p += DTLS_FIN_LENGTH;

  return dtls_send(ctx, peer, DTLS_CT_HANDSHAKE, 
		   ctx->sendbuf, p - ctx->sendbuf);
}

static int
check_server_hello(dtls_context_t *ctx, 
		      dtls_peer_t *peer,
		      uint8 *data, size_t data_length) {
  dtls_hello_verify_t *hv;
  uint8 *p = ctx->sendbuf;
  size_t size;
  int res;
  const dtls_key_t *key;

  /* 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 (IS_SERVERHELLO(data, data_length)) {
    DBG("handle ServerHello");

    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) {
      INFO("Unknown DTLS version");
      goto error;
    }

    data += sizeof(uint16);	      /* skip version field */
    data_length -= sizeof(uint16);

    /* FIXME: check PSK hint */
    if (CALL(ctx, get_key, &peer->session, NULL, 0, &key) < 0
	|| !calculate_key_block(ctx, OTHER_CONFIG(peer), key, 
				OTHER_CONFIG(peer)->client_random, data)) {
      goto error;
    }
    /* store server random data */

    /* memcpy(OTHER_CONFIG(peer)->server_random, data, */
    /* 	   sizeof(OTHER_CONFIG(peer)->server_random)); */
    data += sizeof(OTHER_CONFIG(peer)->client_random);
    data_length -= sizeof(OTHER_CONFIG(peer)->client_random);

    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. */
    OTHER_CONFIG(peer)->cipher = dtls_uint16_to_int(data);
    if (!known_cipher(OTHER_CONFIG(peer)->cipher)) {
      INFO("Unsupported cipher 0x%02x 0x%02x\n",data[0], data[1]);
      goto error;
    }
    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_COMP_NULL) {
      INFO("Unsupported compression method 0x%02x\n", data[0]);
      goto error;
    }

    return 1;
  }

  if (!IS_HELLOVERIFY(data, data_length)) {
    DBG("No HelloVerify");
    return 0;
  }

  DBG("Got HelloVerify");
  hv = (dtls_hello_verify_t *)(data + DTLS_HS_LENGTH);

  /* FIXME: dtls_send_client_hello(ctx,peer,cookie) */
  size = DTLS_CH_LENGTH + 8 + dtls_uint8_to_int(&hv->cookie_length);

  p = dtls_set_handshake_header(DTLS_HT_CLIENT_HELLO, peer, 
				size, 0, size, p);

  dtls_int_to_uint16(p, DTLS_VERSION);
  p += sizeof(uint16);

  /* we must use the same Client Random as for the previous request */
  memcpy(p, OTHER_CONFIG(peer)->client_random, 
	 sizeof(OTHER_CONFIG(peer)->client_random));
  p += sizeof(OTHER_CONFIG(peer)->client_random);

  /* session id (length 0) */
  dtls_int_to_uint8(p, 0);
  p += sizeof(uint8);

  dtls_int_to_uint8(p, dtls_uint8_to_int(&hv->cookie_length));
  p += sizeof(uint8);
  memcpy(p, hv->cookie, dtls_uint8_to_int(&hv->cookie_length));
  p += dtls_uint8_to_int(&hv->cookie_length);

  /* add known cipher(s) */
  dtls_int_to_uint16(p, 2);
  p += sizeof(uint16);
  
  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, 0);
  p += sizeof(uint8);

  update_hs_hash(peer, ctx->sendbuf, p - ctx->sendbuf);

  res = dtls_send(ctx, peer, DTLS_CT_HANDSHAKE, ctx->sendbuf, 
		  p - ctx->sendbuf);
  if (res < 0)
    WARN("cannot send ClientHello");

 error: 
  return 0;
}

static int
check_server_hellodone(dtls_context_t *ctx, 
		      dtls_peer_t *peer,
		      uint8 *data, size_t data_length) {

  /* calculate master key, send CCS */
  if (!IS_SERVERHELLODONE(data, data_length))
    return 0;
  
  update_hs_hash(peer, data, data_length);

  /* set crypto context for TLS_PSK_WITH_AES_128_CCM_8 */
  /* client */
  dtls_cipher_free(OTHER_CONFIG(peer)->read_cipher);

  assert(OTHER_CONFIG(peer)->cipher != TLS_NULL_WITH_NULL_NULL);
  OTHER_CONFIG(peer)->read_cipher = 
    dtls_cipher_new(OTHER_CONFIG(peer)->cipher,
		    dtls_kb_server_write_key(OTHER_CONFIG(peer)),
		    dtls_kb_key_size(OTHER_CONFIG(peer)));

  if (!OTHER_CONFIG(peer)->read_cipher) {
    WARN("Cannot create read cipher");
    return 0;
  }

  dtls_cipher_set_iv(OTHER_CONFIG(peer)->read_cipher,
		     dtls_kb_server_iv(OTHER_CONFIG(peer)),
		     dtls_kb_iv_size(OTHER_CONFIG(peer)));

  /* server */
  dtls_cipher_free(OTHER_CONFIG(peer)->write_cipher);
  
  OTHER_CONFIG(peer)->write_cipher = 
    dtls_cipher_new(OTHER_CONFIG(peer)->cipher,
		    dtls_kb_client_write_key(OTHER_CONFIG(peer)),
		    dtls_kb_key_size(OTHER_CONFIG(peer)));
  
  if (!OTHER_CONFIG(peer)->write_cipher) {
    dtls_cipher_free(OTHER_CONFIG(peer)->read_cipher);
    WARN("Cannot create write cipher");
    return 0;
  }
  
  dtls_cipher_set_iv(OTHER_CONFIG(peer)->write_cipher,
		     dtls_kb_client_iv(OTHER_CONFIG(peer)),
		     dtls_kb_iv_size(OTHER_CONFIG(peer)));

  /* send ClientKeyExchange */
  if (dtls_send_kx(ctx, peer, 1) < 0) {
    DBG("Cannot send KeyExchange message");
    return 0;
  }

  /* and switch cipher suite */
  if (dtls_send_ccs(ctx, peer) < 0) {
    DBG("Cannot send CCS message");
    return 0;
  }

  SWITCH_CONFIG(peer);
  inc_uint(uint16, peer->epoch);
  memset(peer->rseq, 0, sizeof(peer->rseq));
/* #ifndef NDEBUG */
/*   { */
/*       printf("key_block:\n"); */
/*       printf("  client_MAC_secret:\t");   */
/*       dump(dtls_kb_client_mac_secret(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_mac_secret_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  server_MAC_secret:\t");   */
/*       dump(dtls_kb_server_mac_secret(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_mac_secret_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  client_write_key:\t");   */
/*       dump(dtls_kb_client_write_key(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_key_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  server_write_key:\t");   */
/*       dump(dtls_kb_server_write_key(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_key_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  client_IV:\t\t");   */
/*       dump(dtls_kb_client_iv(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_iv_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */
      
/*       printf("  server_IV:\t\t");   */
/*       dump(dtls_kb_server_iv(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_iv_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */
      

/*   } */
/* #endif */

  /* Client Finished */
  {
    DBG("Send Finished");
    int length;
    uint8 buf[DTLS_HMAC_MAX];
    uint8 *p = ctx->sendbuf;

    unsigned char statebuf[DTLS_HASH_CTX_SIZE];

    /* FIXME: adjust message overhead calculation */
    assert(msg_overhead(peer, DTLS_HS_LENGTH + DTLS_FIN_LENGTH) 
	   < sizeof(ctx->sendbuf));

    p = dtls_set_handshake_header(DTLS_HT_FINISHED, 
				  peer, DTLS_FIN_LENGTH, 
				  0, DTLS_FIN_LENGTH, p);
  
    /* temporarily store hash status for roll-back after finalize */
    memcpy(statebuf, &peer->hs_state.hs_hash, DTLS_HASH_CTX_SIZE);

    length = finalize_hs_hash(peer, buf);

    /* restore hash status */
    memcpy(&peer->hs_state.hs_hash, statebuf, DTLS_HASH_CTX_SIZE);

    dtls_prf(CURRENT_CONFIG(peer)->master_secret, 
	     DTLS_MASTER_SECRET_LENGTH,
	     PRF_LABEL(client), PRF_LABEL_SIZE(client),
	     PRF_LABEL(finished), PRF_LABEL_SIZE(finished),
	     buf, length,
	     p, DTLS_FIN_LENGTH);
  
    p += DTLS_FIN_LENGTH;

    update_hs_hash(peer, ctx->sendbuf, p - ctx->sendbuf);
    if (dtls_send(ctx, peer, DTLS_CT_HANDSHAKE, 
		  ctx->sendbuf, p - ctx->sendbuf) < 0) {
      INFO("Cannot send Finished message");
      return 0;
    }
  }
  return 1;
}

int
decrypt_verify(dtls_peer_t *peer,
	       uint8 *packet, size_t length,
	       uint8 **cleartext, size_t *clen) {
  int ok = 0;
  
  *cleartext = (uint8 *)packet + sizeof(dtls_record_header_t);
  *clen = length - sizeof(dtls_record_header_t);

  if (CURRENT_CONFIG(peer)->cipher == TLS_NULL_WITH_NULL_NULL) {
    /* no cipher suite selected */
    return 1;
  } else {			/* TLS_PSK_WITH_AES_128_CCM_8 */   
    dtls_cipher_context_t *cipher_context;
    /** 
     * 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
#define A_DATA N
    unsigned char N[max(DTLS_CCM_BLOCKSIZE, A_DATA_LEN)];
    long int len;


    if (*clen < 16)		/* need at least IV and MAC */
      return -1;

    memset(N, 0, DTLS_CCM_BLOCKSIZE);
    memcpy(N, dtls_kb_remote_iv(CURRENT_CONFIG(peer)), 
	   dtls_kb_iv_size(CURRENT_CONFIG(peer)));

    /* read epoch and seq_num from message */
    memcpy(N + dtls_kb_iv_size(CURRENT_CONFIG(peer)), *cleartext, 8);
    *cleartext += 8;
    *clen -= 8;

    cipher_context = CURRENT_CONFIG(peer)->read_cipher;
    
    if (!cipher_context) {
      WARN("No read_cipher available!");
      return 0;
    }
      
/* #ifndef NDEBUG */
/*     printf("nonce:\t"); */
/*     dump(N, DTLS_CCM_BLOCKSIZE); */
/*     printf("\nkey:\t"); */
/*     dump(dtls_kb_remote_write_key(CURRENT_CONFIG(peer)),  */
/* 	 dtls_kb_key_size(CURRENT_CONFIG(peer))); */
/*     printf("\nciphertext:\n"); */
/*     dump(*cleartext, *clen); */
/*     printf("\n"); */
/* #endif */

    dtls_cipher_set_iv(cipher_context, N, DTLS_CCM_BLOCKSIZE);

    /* 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 */

    len = dtls_decrypt(cipher_context, *cleartext, *clen, *cleartext,
		       A_DATA, A_DATA_LEN);

    ok = len >= 0;
    if (!ok)
      WARN("decryption failed");
    else {
/* #ifndef NDEBUG */
/*       printf("decrypt_verify(): found %ld bytes cleartext\n", len); */
/* #endif */
      *clen = len;
    }
/* #ifndef NDEBUG */
/*     printf("\ncleartext:\n"); */
/*     dump(*cleartext, *clen); */
/*     printf("\n"); */
/* #endif */
  }

  return ok;
}


int
handle_handshake(dtls_context_t *ctx, dtls_peer_t *peer, 
		 uint8 *record_header, uint8 *data, size_t data_length) {

  /* 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. */

  switch (peer->state) {

  /************************************************************************
   * Client states
   ************************************************************************/

  case DTLS_STATE_CLIENTHELLO:
    /* here we expect a HelloVerify or ServerHello */

    DBG("DTLS_STATE_CLIENTHELLO");
    if (check_server_hello(ctx, peer, data, data_length)) {
      peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
    /* update_hs_hash(peer, data, data_length); */
    }

    break;

  case DTLS_STATE_WAIT_SERVERHELLODONE:
    /* expect a ServerHelloDone */

    DBG("DTLS_STATE_WAIT_SERVERHELLODONE");

    if (check_server_hellodone(ctx, peer, data, data_length)) {
      peer->state = DTLS_STATE_WAIT_SERVERFINISHED;
      /* update_hs_hash(peer, data, data_length); */
    }

    break;

  case DTLS_STATE_WAIT_SERVERFINISHED:
    /* expect a Finished message from server */

    DBG("DTLS_STATE_WAIT_SERVERFINISHED");
    if (check_finished(ctx, peer, record_header, data, data_length)) {
      DBG("finished!");
      peer->state = DTLS_STATE_CONNECTED;
    }

    break;

  /************************************************************************
   * Server states
   ************************************************************************/

  case DTLS_STATE_SERVERHELLO:
    /* here we expect a ClientHello */
    /* handle ClientHello, update msg and msglen and goto next if not finished */

    DBG("DTLS_STATE_SERVERHELLO");
    if (!check_client_keyexchange(ctx, peer, data, data_length)) {
      WARN("check_client_keyexchange failed (%d, %d)", data_length, data[0]);
      return 0;			/* drop it, whatever it is */
    }
    
    update_hs_hash(peer, data, data_length);
    peer->state = DTLS_STATE_KEYEXCHANGE;
    break;

  case DTLS_STATE_WAIT_FINISHED:
    DBG("DTLS_STATE_WAIT_FINISHED");
    if (check_finished(ctx, peer, record_header, data, data_length)) {
      DBG("finished!");
	
      /* send ServerFinished */
      update_hs_hash(peer, data, data_length);

      if (dtls_send_server_finished(ctx, peer) > 0) {
	peer->state = DTLS_STATE_CONNECTED;
      } else {
	WARN("sending server Finished failed");
      }
    } else {
      /* send alert */
    }
    break;
      
  case DTLS_STATE_CONNECTED:
    /* At this point, we have a good relationship with this peer. This
     * state is left for re-negotiation of key material. */
    
    DBG("DTLS_STATE_CONNECTED");

    /* renegotiation */
    if (dtls_verify_peer(ctx, peer, &peer->session, 
			 record_header, data, data_length) > 0) {

      clear_hs_hash(peer);

      if (!dtls_update_parameters(ctx, peer, data, data_length)) {
	
	WARN("Error updating security parameters");
	dtls_alert(ctx, peer, DTLS_ALERT_LEVEL_WARNING, 
		   DTLS_ALERT_NO_RENEGOTIATION);
	return 0;
      }

      /* update finish MAC */
      update_hs_hash(peer, data, data_length); 

      if (dtls_send_server_hello(ctx, peer) > 0)
	peer->state = DTLS_STATE_SERVERHELLO;
    
      /* after sending the ServerHelloDone, we expect the 
       * ClientKeyExchange (possibly containing the PSK id),
       * followed by a ChangeCipherSpec and an encrypted Finished.
       */
    }

    break;
    
  case DTLS_STATE_INIT:	      /* these states should not occur here */
  case DTLS_STATE_KEYEXCHANGE:
  default:
    INFO("Unhandled state %d", peer->state);
    assert(0);
  }

  return 1;
}

int
handle_ccs(dtls_context_t *ctx, dtls_peer_t *peer, 
	   uint8 *record_header, uint8 *data, size_t data_length) {

  /* 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->state != DTLS_STATE_KEYEXCHANGE
      || !check_ccs(ctx, peer, record_header, data, data_length)) {
    /* signal error? */
    WARN("Expected ChangeCipherSpec during handshake");
    return 0;

  }

  /* send change cipher spec message and switch to new configuration */
  if (dtls_send_ccs(ctx, peer) < 0) {
    WARN("Cannot send CCS message");
    return 0;
  } 
  
  SWITCH_CONFIG(peer);
  inc_uint(uint16, peer->epoch);
  memset(peer->rseq, 0, sizeof(peer->rseq));
  
  peer->state = DTLS_STATE_WAIT_FINISHED;

/* #ifndef NDEBUG */
/*   { */
/*       printf("key_block:\n"); */
/*       printf("  client_MAC_secret:\t");   */
/*       dump(dtls_kb_client_mac_secret(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_mac_secret_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  server_MAC_secret:\t");   */
/*       dump(dtls_kb_server_mac_secret(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_mac_secret_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  client_write_key:\t");   */
/*       dump(dtls_kb_client_write_key(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_key_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  server_write_key:\t");   */
/*       dump(dtls_kb_server_write_key(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_key_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */

/*       printf("  client_IV:\t\t");   */
/*       dump(dtls_kb_client_iv(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_iv_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */
      
/*       printf("  server_IV:\t\t");   */
/*       dump(dtls_kb_server_iv(CURRENT_CONFIG(peer)),  */
/* 	   dtls_kb_iv_size(CURRENT_CONFIG(peer))); */
/*       printf("\n"); */
      

/*   } */
/* #endif */

  return 1;
}  

/** 
 * Handles incoming Alert messages. This function returns \c 1 if the
 * connection should be closed and the peer is to be invalidated.
 */
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 */

  if (data_length < 2)
    return 0;

  INFO("** Alert: level %d, description %d\n", data[0], data[1]);

  /* 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) {
    INFO("%d invalidate peer\n", data[1]);
    
#ifndef WITH_CONTIKI
    HASH_DEL_PEER(ctx->peers, peer);
#else /* WITH_CONTIKI */
    list_remove(ctx->peers, peer);

/* #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:
    /* 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_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE);
    } else
      peer->state = DTLS_STATE_CLOSED;
    break;
  default:
    ;
  }
  
  if (free_peer) {
    dtls_stop_retransmission(ctx, peer);
    dtls_free_peer(peer);
  }

  return free_peer;
}

/** 
 * Handles incoming data as DTLS message from given peer.
 */
int dtls_handle_message(
   dtls_context_t *ctx,
   session_t *session,
   uint8 *msg, 
   int msglen) {
		    
  DBG("dtls_handle_message");
  dtls_peer_t *peer = NULL;
  unsigned int rlen;		/* record length */
  uint8 *data; 			/* (decrypted) payload */
  size_t data_length;		/* length of decrypted payload 
				   (without MAC and padding) */

  /* check if we have DTLS state for addr/port/ifindex */
  
  DBG("Check for cached DTLS state for addr/port/ifindex");
  peer = dtls_get_peer(ctx, session);

#ifndef NDEBUG
  if (peer) {
    unsigned char addrbuf[72];

    dsrv_print_addr(session, addrbuf, sizeof(addrbuf));
    DBG("Found peer \"%s\"", addrbuf);
  }
#endif /* NDEBUG */

  if (!peer) {			
    DBG("No peer found.");
    /* get first record from client message */
    rlen = is_record(msg, msglen);
    assert(rlen <= msglen);

    if (!rlen) {
#ifndef NDEBUG
      if (msglen > 3) 
	DBG("Dropped invalid message %02x%02x%02x%02x", msg[0], msg[1], msg[2], msg[3]);
      else
	DBG("Dropped invalid message (less than four bytes)");
#endif
      return 0;
    }

    /* is_record() ensures that msg contains at least a record header */
    data = msg + DTLS_RH_LENGTH;
    data_length = rlen - DTLS_RH_LENGTH;

    /* 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.
    */

    if (dtls_verify_peer(ctx, NULL, session, msg, data, data_length) <= 0) {
      WARN("Cannot verify peer.");
      return -1;
    }
    
    /* 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) {
      DBG("Cannot create peer.");
      /* FIXME: signal internal error */
      return -1;
    }
    DBG("Created new peer.");

    /* Initialize record sequence number to 1 for new peers. The first
     * record with sequence number 0 is a stateless Hello Verify Request.
     */
    peer->rseq[5] = 1;

    /* 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.
     */
    if (!dtls_update_parameters(ctx, peer, 
			msg + DTLS_RH_LENGTH, rlen - DTLS_RH_LENGTH)) {

      WARN("Error updating security parameters");
      /* FIXME: send handshake failure Alert */
      dtls_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, 
		 DTLS_ALERT_HANDSHAKE_FAILURE);
      dtls_free_peer(peer);
      return -1;
    }
#ifndef WITH_CONTIKI
    DBG("Adding peer to hash");
    HASH_ADD_PEER(ctx->peers, session, peer);
#else /* WITH_CONTIKI */
    list_add(ctx->peers, peer);
#endif /* WITH_CONTIKI */
    
    /* update finish MAC */
    update_hs_hash(peer, msg + DTLS_RH_LENGTH, rlen - DTLS_RH_LENGTH); 
 
    if (dtls_send_server_hello(ctx, peer) > 0)
      peer->state = DTLS_STATE_SERVERHELLO;
    
    /* after sending the ServerHelloDone, we expect the 
     * ClientKeyExchange (possibly containing the PSK id),
     * followed by a ChangeCipherSpec and an encrypted Finished.
     */

    msg += rlen;
    msglen -= rlen;
  } else {
    //DBG("Found peer");
  }

  /* At this point peer contains a state machine to handle the
     received message. */

  assert(peer);

  /* FIXME: check sequence number of record and drop message if the
   * number is not exactly the last number that we have responded to + 1. 
   * Otherwise, stop retransmissions for this specific peer and 
   * continue processing. */
  dtls_stop_retransmission(ctx, peer);

  while ((rlen = is_record(msg,msglen))) {

    DBG("Got packet %d (%d bytes)", msg[0], rlen);
    /* skip packet if it is from a different epoch */
    if (memcmp(DTLS_RECORD_HEADER(msg)->epoch, 
	       peer->epoch, sizeof(uint16)) != 0)
      goto next;

    if (!decrypt_verify(peer, msg, rlen, &data, &data_length)) {
      INFO("decrypt_verify() failed");
      goto next;
    }

/* #ifndef NDEBUG */
/*     hexdump(msg, sizeof(dtls_record_header_t)); */
/*     printf("\n"); */
/*     hexdump(data, data_length); */
/*     printf("\n"); */
/* #endif */

    /* 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:
      handle_ccs(ctx, peer, msg, data, data_length);
      break;

    case DTLS_CT_ALERT:
      if (handle_alert(ctx, peer, msg, data, data_length)) {
	/* handle alert has invalidated peer */
	peer = NULL;
	return 0;
      }

    case DTLS_CT_HANDSHAKE:
      DBG("case DTLS_CT_HANDSHAKE");
      handle_handshake(ctx, peer, msg, data, data_length);
      if (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:
      INFO("** application data:");
      CALL(ctx, read, &peer->session, data, data_length);
      break;
    default:
      INFO("dropped unknown message of type %d",msg[0]);
    }

  next:
    /* 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;
  // XXX there is no /dev/urandom so this needs to be sorted
#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 */
  prng_init(now);
#else /* WITH_CONTIKI */

// urandom is only used to init a buffer, XXX fixme too
// need a decent urandom
/*

  if (!urandom) {
    dsrv_log(LOG_EMERG, "cannot initialize PRNG\n");
    return NULL;
  }

  if (fread(buf, 1, sizeof(buf), urandom) != sizeof(buf)) {
    dsrv_log(LOG_EMERG, "cannot initialize PRNG\n");
    return NULL;
  }

  fclose(urandom);
  */
  // just randomly flip some bits
  unsigned long mask = 0x0000;
  for(int i=0; i<sizeof(buf); i++) {
     // XXX need to flip some bits
  }
  prng_init((unsigned long)*buf);
#endif /* WITH_CONTIKI */

  c = &the_dtls_context;

  memset(c, 0, sizeof(dtls_context_t));
  c->app = app_data;
  
  LIST_STRUCT_INIT(c, sendqueue);

#ifdef WITH_CONTIKI
  LIST_STRUCT_INIT(c, peers);
  /* LIST_STRUCT_INIT(c, key_store); */
  
  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 (prng(c->cookie_secret, DTLS_COOKIE_SECRET_LENGTH))
    c->cookie_secret_age = now;
  else 
    goto error;
  
  return c;

 error:
  INFO("Cannot create DTLS context. Fail.");
  if (c)
    dtls_free_context(c);
  return NULL;
}

void dtls_free_context(dtls_context_t *ctx) {
  dtls_peer_t *p;
  
#ifndef WITH_CONTIKI
  dtls_peer_t *tmp;

  if (ctx->peers) {
    HASH_ITER(hh, ctx->peers, p, tmp) {
      dtls_free_peer(p);
    }
  }
#else /* WITH_CONTIKI */
  int i;

  p = (dtls_peer_t *)peer_storage.mem;
  for (i = 0; i < peer_storage.num; ++i, ++p) {
    if (peer_storage.count[i])
      dtls_free_peer(p);
  }
#endif /* WITH_CONTIKI */
}

int
dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
  uint8 *p = ctx->sendbuf;
  size_t size;
  int res;
  dtls_tick_t now;

  assert(peer);
  if (!peer)
    return -1;

  /* check if the same peer is already in our list */
  if (peer == dtls_get_peer(ctx, &peer->session)) {
    DBG("Found peer, try to re-connect");
    /* FIXME: send HelloRequest if we are server, 
       ClientHello with good cookie if client */
    return 0;
  }
    
  /* set peer role to server: */
  OTHER_CONFIG(peer)->role = DTLS_SERVER;
  CURRENT_CONFIG(peer)->role = DTLS_SERVER;

  dtls_add_peer(ctx, peer);

  /* send ClientHello with some Cookie */

  /* add to size:
   *   1. length of session id (including length field)
   *   2. length of cookie (including length field)
   *   3. cypher suites
   *   4. compression methods 
   */
  size = DTLS_CH_LENGTH + 8;

  /* force sending 0 as handshake message sequence number by setting
   * peer to NULL */
  p = dtls_set_handshake_header(DTLS_HT_CLIENT_HELLO, NULL, 
				size, 0, size, p);

  dtls_int_to_uint16(p, DTLS_VERSION);
  p += sizeof(uint16);

  dtls_ticks(&now);
  /* Set client random: First 4 bytes are the client's Unix timestamp,
   * followed by 28 bytes of generate random data. */
  dtls_int_to_uint32(&OTHER_CONFIG(peer)->client_random, 
		     now / DTLS_TICKS_PER_SECOND);
  prng(OTHER_CONFIG(peer)->client_random + sizeof(uint32),
       sizeof(OTHER_CONFIG(peer)->client_random) - sizeof(uint32));
  memcpy(p, OTHER_CONFIG(peer)->client_random, 
	 sizeof(OTHER_CONFIG(peer)->client_random));
  p += 32;

  /* session id (length 0) */
  dtls_int_to_uint8(p, 0);
  p += sizeof(uint8);

  dtls_int_to_uint8(p, 0);
  p += sizeof(uint8);

  /* add supported cipher suite */
  dtls_int_to_uint16(p, 2);
  p += sizeof(uint16);
  
  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_COMP_NULL);
  p += sizeof(uint8);

  res = dtls_send(ctx, peer, DTLS_CT_HANDSHAKE, ctx->sendbuf, 
		  p - ctx->sendbuf);
  if (res < 0)
    WARN("Cannot send ClientHello");
  else 
    peer->state = DTLS_STATE_CLIENTHELLO;

  DBG("Sent client hello");
  return res;
}

int
dtls_connect(dtls_context_t *ctx, const session_t *dst) {
  DBG("dtls_connect");
  dtls_peer_t *peer;

  peer = dtls_get_peer(ctx, dst);
  
  if (!peer)
    peer = dtls_new_peer(dst);

  DBG("Created new peer as it didn't exist.");

  if (!peer) {
    DBG("Cannot create new peer, bailing.");
    return -1;
  }

  return dtls_connect_peer(ctx, peer);
}

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);

      node->retransmit_cnt++;
      node->t += (node->timeout << node->retransmit_cnt);
      netq_insert_node((netq_t **)context->sendqueue, node);
      
      DBG("** retransmit packet");
      
      if (dtls_prepare_record(node->peer, DTLS_CT_HANDSHAKE, 
			      node->data, node->length, 
			      sendbuf, &len) > 0) {
	
#ifndef NDEBUG
	if (dtls_get_log_level() >= LOG_DEBUG) {
	  DBG("retransmit %d bytes", len);
	  hexdump(sendbuf, sizeof(dtls_record_header_t));
	  DBGX("\r\n");
	  hexdump(node->data, node->length);
	  DBGX("\r\n");
	}
#endif
	
	(void)CALL(context, write, &node->peer->session, sendbuf, len);
      }
      return;
  }

  /* no more retransmissions, remove node from system */
  
  DBG("** removed transaction");

  /* And finally delete the node */
  netq_node_free(node);
}

void
dtls_stop_retransmission(dtls_context_t *context, dtls_peer_t *peer) {
  void *node;
  node = list_head((list_t)context->sendqueue); 

  while (node) {
    if (dtls_session_equals(&((netq_t *)node)->peer->session,
			    &peer->session)) {
      void *tmp = node;
      node = list_item_next(node);
      list_remove((list_t)context->sendqueue, tmp);
      netq_node_free((netq_t *)tmp);
    } else
      node = list_item_next(node);    
  }
}

void
dtls_check_retransmit(dtls_context_t *context, clock_time_t *next) {
  dtls_tick_t now;
  netq_t *node = netq_head((netq_t **)context->sendqueue);

  dtls_ticks(&now);
  while (node && node->t <= now) {
    netq_pop_first((netq_t **)context->sendqueue);
    dtls_retransmit(context, node);
    node = netq_head((netq_t **)context->sendqueue);
  }

  if (next && node)
    *next = node->t;
}

#ifdef WITH_CONTIKI
/*---------------------------------------------------------------------------*/
/* message retransmission */
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(dtls_retransmit_process, ev, data)
{
  clock_time_t now;
  netq_t *node;

  PROCESS_BEGIN();

  DBG("Started DTLS retransmit process");

  while(1) {
    PROCESS_YIELD();
    if (ev == PROCESS_EVENT_TIMER) {
      if (etimer_expired(&the_dtls_context.retransmit_timer)) {
	
	node = list_head(the_dtls_context.sendqueue);
	
	now = clock_time();
	while (node && node->t <= now) {
	  dtls_retransmit(&the_dtls_context, list_pop(the_dtls_context.sendqueue));
	  node = list_head(the_dtls_context.sendqueue);
	}

	/* need to set timer to some value even if no nextpdu is available */
	etimer_set(&the_dtls_context.retransmit_timer, 
		   node ? node->t - now : 0xFFFF);
      } 
    }
  }
  
  PROCESS_END();
}
#endif /* WITH_CONTIKI */

#ifndef NDEBUG
/** dumps packets in usual hexdump format */
void hexdump(const unsigned char *packet, int length) {
  int n = 0;

  while (length--) { 
    if (n % 16 == 0)
      printf("%08X ",n);

    printf("%02X ", *packet++);
    
    n++;
    if (n % 8 == 0) {
      if (n % 16 == 0)
	     printf("\r\n");
      else
	     printf(" ");
    }
  }
}

/** dump as narrow string of hex digits */
void dump(unsigned char *buf, size_t len) {
  while (len--) 
    printf("%02x", *buf++);
}
#endif