mbed port of tinydtls

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

#include <stdio.h>
#ifdef HAVE_ASSERT_H
#include <assert.h>
#endif

#include "global.h"
#include "debug.h"
#include "numeric.h"
#include "dtls.h"
#include "crypto.h"
#include "ccm.h"

#ifndef WITH_CONTIKI
#include <stdlib.h>

static inline dtls_cipher_context_t *
dtls_cipher_context_new() {
  return (dtls_cipher_context_t *)malloc(sizeof(dtls_cipher_context_t));
}

static inline void
dtls_cipher_context_free(dtls_cipher_context_t *ctx) {
  free(ctx);
}
#else /* WITH_CONTIKI */
MEMB(cipher_storage, dtls_cipher_context_t, DTLS_CIPHER_CONTEXT_MAX);

static inline dtls_cipher_context_t *
dtls_cipher_context_new() {
  return (dtls_cipher_context_t *)memb_alloc(&cipher_storage);
}

static inline void
dtls_cipher_context_free(dtls_cipher_context_t *ctx) {
  if (ctx)
    memb_free(&cipher_storage, ctx);
}
#endif /* WITH_CONTIKI */

extern void dtls_hmac_storage_init();

void crypto_init() {
  dtls_hmac_storage_init();

#ifdef WITH_CONTIKI
  memb_init(&cipher_storage);
#endif /* WITH_CONTIKI */
}

#ifndef NDEBUG
extern void dump(unsigned char *, size_t);
#endif

#define HMAC_UPDATE_SEED(Context,Seed,Length)		\
  if (Seed) dtls_hmac_update(Context, (Seed), (Length))

size_t
dtls_p_hash(dtls_hashfunc_t h,
	    const unsigned char *key, size_t keylen,
	    const unsigned char *label, size_t labellen,
	    const unsigned char *random1, size_t random1len,
	    const unsigned char *random2, size_t random2len,
	    unsigned char *buf, size_t buflen) {
  dtls_hmac_context_t *hmac_a, *hmac_p;

  unsigned char A[DTLS_HMAC_DIGEST_SIZE];
  unsigned char tmp[DTLS_HMAC_DIGEST_SIZE];
  size_t dlen;			/* digest length */
  size_t len = 0;			/* result length */

  hmac_a = dtls_hmac_new(key, keylen);
  if (!hmac_a)
    return 0;

  /* calculate A(1) from A(0) == seed */
  HMAC_UPDATE_SEED(hmac_a, label, labellen);
  HMAC_UPDATE_SEED(hmac_a, random1, random1len);
  HMAC_UPDATE_SEED(hmac_a, random2, random2len);

  dlen = dtls_hmac_finalize(hmac_a, A);

  hmac_p = dtls_hmac_new(key, keylen);
  if (!hmac_p)
    goto error;

  while (len + dlen < buflen) {

    /* FIXME: rewrite loop to avoid superflous call to dtls_hmac_init() */
    dtls_hmac_init(hmac_p, key, keylen);
    dtls_hmac_update(hmac_p, A, dlen);

    HMAC_UPDATE_SEED(hmac_p, label, labellen);
    HMAC_UPDATE_SEED(hmac_p, random1, random1len);
    HMAC_UPDATE_SEED(hmac_p, random2, random2len);

    len += dtls_hmac_finalize(hmac_p, tmp);
    memcpy(buf, tmp, dlen);
    buf += dlen;

    /* calculate A(i+1) */
    dtls_hmac_init(hmac_a, key, keylen);
    dtls_hmac_update(hmac_a, A, dlen);
    dtls_hmac_finalize(hmac_a, A);
  }

  dtls_hmac_init(hmac_p, key, keylen);
  dtls_hmac_update(hmac_p, A, dlen);
  
  HMAC_UPDATE_SEED(hmac_p, label, labellen);
  HMAC_UPDATE_SEED(hmac_p, random1, random1len);
  HMAC_UPDATE_SEED(hmac_p, random2, random2len);
  
  dtls_hmac_finalize(hmac_p, tmp);
  memcpy(buf, tmp, buflen - len);

 error:
  dtls_hmac_free(hmac_a);
  dtls_hmac_free(hmac_p);

  return buflen;
}

size_t 
dtls_prf(const unsigned char *key, size_t keylen,
	 const unsigned char *label, size_t labellen,
	 const unsigned char *random1, size_t random1len,
	 const unsigned char *random2, size_t random2len,
	 unsigned char *buf, size_t buflen) {

  /* Clear the result buffer */
  memset(buf, 0, buflen);
  return dtls_p_hash(HASH_SHA256, 
		     key, keylen, 
		     label, labellen, 
		     random1, random1len,
		     random2, random2len,
		     buf, buflen);
}

void
dtls_mac(dtls_hmac_context_t *hmac_ctx, 
	 const unsigned char *record,
	 const unsigned char *packet, size_t length,
	 unsigned char *buf) {
  uint16 L;
  dtls_int_to_uint16(L, length);

  assert(hmac_ctx);
  dtls_hmac_update(hmac_ctx, record +3, sizeof(uint16) + sizeof(uint48));
  dtls_hmac_update(hmac_ctx, record, sizeof(uint8) + sizeof(uint16));
  dtls_hmac_update(hmac_ctx, L, sizeof(uint16));
  dtls_hmac_update(hmac_ctx, packet, length);
  
  dtls_hmac_finalize(hmac_ctx, buf);
}

static inline void
dtls_ccm_init(aes128_ccm_t *ccm_ctx, unsigned char *N, size_t length) {
  assert(ccm_ctx);

  if (length < DTLS_CCM_BLOCKSIZE)
    memset(ccm_ctx->N + length, 0, DTLS_CCM_BLOCKSIZE - length);

  memcpy(ccm_ctx->N, N, DTLS_CCM_BLOCKSIZE);
}

size_t
dtls_ccm_encrypt(aes128_ccm_t *ccm_ctx, const unsigned char *src, size_t srclen,
		 unsigned char *buf, 
		 const unsigned char *aad, size_t la) {
  long int len;

  assert(ccm_ctx);

  len = dtls_ccm_encrypt_message(&ccm_ctx->ctx, 8 /* M */, 
				 max(2, 15 - DTLS_CCM_NONCE_SIZE),
				 ccm_ctx->N,
				 buf, srclen, 
				 aad, la);
  return len;
}

size_t
dtls_ccm_decrypt(aes128_ccm_t *ccm_ctx, const unsigned char *src,
		 size_t srclen, unsigned char *buf,
		 const unsigned char *aad, size_t la) {
  long int len;

  assert(ccm_ctx);

  len = dtls_ccm_decrypt_message(&ccm_ctx->ctx, 8 /* M */, 
				 max(2, 15 - DTLS_CCM_NONCE_SIZE),
				 ccm_ctx->N, 
				 buf, srclen, 
				 aad, la);
  return len;
}

size_t
dtls_pre_master_secret(unsigned char *key, size_t keylen,
		       unsigned char *result) {
  unsigned char *p = result;

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

  memset(p, 0, keylen);
  p += keylen;

  memcpy(p, result, sizeof(uint16));
  p += sizeof(uint16);
  
  memcpy(p, key, keylen);

  return (sizeof(uint16) + keylen) << 1;
}

void 
dtls_cipher_set_iv(dtls_cipher_context_t *ctx,
		   unsigned char *iv, size_t length) {
  assert(ctx);
  dtls_ccm_init(&ctx->data, iv, length);
}

dtls_cipher_context_t *
dtls_cipher_new(dtls_cipher_t cipher,
		unsigned char *key, size_t keylen) {
  dtls_cipher_context_t *cipher_context = NULL;

  cipher_context = dtls_cipher_context_new();
  if (!cipher_context) {
    warn2("cannot allocate cipher_context\r\n");
    return NULL;
  }

  switch (cipher) {
  case TLS_PSK_WITH_AES_128_CCM_8: {
    aes128_ccm_t *ccm_ctx = &cipher_context->data;
    
    if (rijndael_set_key_enc_only(&ccm_ctx->ctx, key, 8 * keylen) < 0) {
      /* cleanup everything in case the key has the wrong size */
      warn2("cannot set rijndael key\n");
      goto error;
    }
    break;
  }
  default:
    warn2("unknown cipher %04x\n", cipher);
    goto error;
  }
  
  return cipher_context;
 error:
  dtls_cipher_context_free(cipher_context);
  return NULL;
}

void 
dtls_cipher_free(dtls_cipher_context_t *cipher_context) {
  dtls_cipher_context_free(cipher_context);
}

int 
dtls_encrypt(dtls_cipher_context_t *ctx, 
	     const unsigned char *src, size_t length,
	     unsigned char *buf,
	     const unsigned char *aad, size_t la) {
  if (ctx) {
    if (src != buf)
      memmove(buf, src, length);
    return dtls_ccm_encrypt(&ctx->data, src, length, buf, 
			    aad, la);
  }

  return -1;
}

int 
dtls_decrypt(dtls_cipher_context_t *ctx, 
	     const unsigned char *src, size_t length,
	     unsigned char *buf,
	     const unsigned char *aad, size_t la) {
  if (ctx) {
    if (src != buf)
      memmove(buf, src, length);
    return dtls_ccm_decrypt(&ctx->data, src, length, buf,
			    aad, la);
  }

  return -1;
}