Version 0.5.0 of tinydtls
Dependents: tinydtls_test_cellular tinydtls_test_ethernet tiny-dtls
Diff: crypto.c
- Revision:
- 0:ff9ebe0cf0e9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crypto.c Fri Oct 18 13:18:30 2013 +0000 @@ -0,0 +1,482 @@ +/* dtls -- a very basic DTLS implementation + * + * Copyright (C) 2011--2012 Olaf Bergmann <bergmann@tzi.org> + * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de> + * + * 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" +#include "ecc/ecc.h" + +#ifndef WITH_CONTIKI +#include <stdlib.h> + +#define __DEBUG__ 4 +#ifndef __MODULE__ +#define __MODULE__ "crypto.c" +#endif +#include "dbg.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 */ + +void crypto_init() { + dtls_hmac_storage_init(); + +#ifdef WITH_CONTIKI + memb_init(&cipher_storage); +#endif /* WITH_CONTIKI */ +} + +#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 size_t +dtls_ccm_encrypt(aes128_ccm_t *ccm_ctx, const unsigned char *src, size_t srclen, + unsigned char *buf, + unsigned char *nounce, + 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), + nounce, + buf, srclen, + aad, la); + return len; +} + +static size_t +dtls_ccm_decrypt(aes128_ccm_t *ccm_ctx, const unsigned char *src, + size_t srclen, unsigned char *buf, + unsigned char *nounce, + 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), + nounce, + buf, srclen, + aad, la); + return len; +} + +size_t +dtls_psk_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; +} + +static void dtls_ec_key_to_uint32(const unsigned char *key, size_t key_size, + uint32_t *result) { + int i; + + for (i = (key_size / sizeof(uint32_t)) - 1; i >= 0 ; i--) { + *result = dtls_uint32_to_int(&key[i * sizeof(uint32_t)]); + result++; + } +} + +static void dtls_ec_key_from_uint32(const uint32_t *key, size_t key_size, + unsigned char *result) { + int i; + + for (i = (key_size / sizeof(uint32_t)) - 1; i >= 0 ; i--) { + dtls_int_to_uint32(result, key[i]); + result += 4; + } +} + +int dtls_ec_key_from_uint32_asn1(const uint32_t *key, size_t key_size, + unsigned char *buf) { + int i; + unsigned char *buf_orig = buf; + int first = 1; + + for (i = (key_size / sizeof(uint32_t)) - 1; i >= 0 ; i--) { + if (key[i] == 0) + continue; + /* the first bit has to be set to zero, to indicate a poritive integer */ + if (first && key[i] & 0x80000000) { + *buf = 0; + buf++; + dtls_int_to_uint32(buf, key[i]); + buf += 4; + } else if (first && !(key[i] & 0xFF800000)) { + buf[0] = (key[i] >> 16) & 0xff; + buf[1] = (key[i] >> 8) & 0xff; + buf[2] = key[i] & 0xff; + buf += 3; + } else if (first && !(key[i] & 0xFFFF8000)) { + buf[0] = (key[i] >> 8) & 0xff; + buf[1] = key[i] & 0xff; + buf += 2; + } else if (first && !(key[i] & 0xFFFFFF80)) { + buf[0] = key[i] & 0xff; + buf += 1; + } else { + dtls_int_to_uint32(buf, key[i]); + buf += 4; + } + first = 0; + } + return buf - buf_orig; +} + +size_t dtls_ecdh_pre_master_secret(unsigned char *priv_key, + unsigned char *pub_key_x, + unsigned char *pub_key_y, + size_t key_size, + unsigned char *result) { + uint32_t priv[8]; + uint32_t pub_x[8]; + uint32_t pub_y[8]; + uint32_t result_x[8]; + uint32_t result_y[8]; + + dtls_ec_key_to_uint32(priv_key, key_size, priv); + dtls_ec_key_to_uint32(pub_key_x, key_size, pub_x); + dtls_ec_key_to_uint32(pub_key_y, key_size, pub_y); + + ecc_ecdh(pub_x, pub_y, priv, result_x, result_y); + + dtls_ec_key_from_uint32(result_x, key_size, result); + return key_size; +} + +void +dtls_ecdsa_generate_key(unsigned char *priv_key, + unsigned char *pub_key_x, + unsigned char *pub_key_y, + size_t key_size) { + uint32_t priv[8]; + uint32_t pub_x[8]; + uint32_t pub_y[8]; + + do { + prng((unsigned char *)priv, key_size); + } while (!ecc_is_valid_key(priv)); + + ecc_gen_pub_key(priv, pub_x, pub_y); + + dtls_ec_key_from_uint32(priv, key_size, priv_key); + dtls_ec_key_from_uint32(pub_x, key_size, pub_key_x); + dtls_ec_key_from_uint32(pub_y, key_size, pub_key_y); +} + +/* rfc4492#section-5.4 */ +void +dtls_ecdsa_create_sig_hash(const unsigned char *priv_key, size_t key_size, + const unsigned char *sign_hash, size_t sign_hash_size, + uint32_t point_r[9], uint32_t point_s[9]) { + int ret; + uint32_t priv[8]; + uint32_t hash[8]; + uint32_t rand[8]; + + dtls_ec_key_to_uint32(priv_key, key_size, priv); + dtls_ec_key_to_uint32(sign_hash, sign_hash_size, hash); + do { + prng((unsigned char *)rand, key_size); + ret = ecc_ecdsa_sign(priv, hash, rand, point_r, point_s); + } while (ret); +} + +void +dtls_ecdsa_create_sig(const unsigned char *priv_key, size_t key_size, + const unsigned char *client_random, size_t client_random_size, + const unsigned char *server_random, size_t server_random_size, + const unsigned char *keyx_params, size_t keyx_params_size, + uint32_t point_r[9], uint32_t point_s[9]) { + dtls_hash_ctx data; + unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE]; + + dtls_hash_init(&data); + dtls_hash_update(&data, client_random, client_random_size); + dtls_hash_update(&data, server_random, server_random_size); + dtls_hash_update(&data, keyx_params, keyx_params_size); + dtls_hash_finalize(sha256hash, &data); + + dtls_ecdsa_create_sig_hash(priv_key, key_size, sha256hash, + sizeof(sha256hash), point_r, point_s); +} + +/* rfc4492#section-5.4 */ +int +dtls_ecdsa_verify_sig_hash(const unsigned char *pub_key_x, + const unsigned char *pub_key_y, size_t key_size, + const unsigned char *sign_hash, size_t sign_hash_size, + unsigned char *result_r, unsigned char *result_s) { + uint32_t pub_x[8]; + uint32_t pub_y[8]; + uint32_t hash[8]; + uint32_t point_r[8]; + uint32_t point_s[8]; + + dtls_ec_key_to_uint32(pub_key_x, key_size, pub_x); + dtls_ec_key_to_uint32(pub_key_y, key_size, pub_y); + dtls_ec_key_to_uint32(result_r, key_size, point_r); + dtls_ec_key_to_uint32(result_s, key_size, point_s); + dtls_ec_key_to_uint32(sign_hash, sign_hash_size, hash); + + return ecc_ecdsa_validate(pub_x, pub_y, hash, point_r, point_s); +} + +int +dtls_ecdsa_verify_sig(const unsigned char *pub_key_x, + const unsigned char *pub_key_y, size_t key_size, + const unsigned char *client_random, size_t client_random_size, + const unsigned char *server_random, size_t server_random_size, + const unsigned char *keyx_params, size_t keyx_params_size, + unsigned char *result_r, unsigned char *result_s) { + dtls_hash_ctx data; + unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE]; + + dtls_hash_init(&data); + dtls_hash_update(&data, client_random, client_random_size); + dtls_hash_update(&data, server_random, server_random_size); + dtls_hash_update(&data, keyx_params, keyx_params_size); + dtls_hash_finalize(sha256hash, &data); + + return dtls_ecdsa_verify_sig_hash(pub_key_x, pub_key_y, key_size, sha256hash, + sizeof(sha256hash), result_r, result_s); +} + +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) { + WARN("cannot allocate cipher_context\r\n"); + return NULL; + } + + switch (cipher) { + case TLS_PSK_WITH_AES_128_CCM_8: + case TLS_ECDHE_ECDSA_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 */ + WARN("cannot set rijndael key\n"); + goto error; + } + break; + } + default: + WARN("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, + unsigned char *nounce, + 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, nounce, + aad, la); + } + + return -1; +} + +int +dtls_decrypt(dtls_cipher_context_t *ctx, + const unsigned char *src, size_t length, + unsigned char *buf, + unsigned char *nounce, + 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, nounce, + aad, la); + } + + return -1; +} +