1312 lines
38 KiB
C
1312 lines
38 KiB
C
/*
|
|
* coap_tinydtls.c -- Datagram Transport Layer Support for libcoap with tinydtls
|
|
*
|
|
* Copyright (C) 2016-2020 Olaf Bergmann <bergmann@tzi.org>
|
|
* Copyright (C) 2020-2022 Jon Shallow <supjps-libcoap@jpshallow.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* This file is part of the CoAP library libcoap. Please see README for terms
|
|
* of use.
|
|
*/
|
|
|
|
/**
|
|
* @file coap_tinydtls.c
|
|
* @brief TinyDTLS specific handling functions
|
|
*/
|
|
|
|
#include "coap3/coap_internal.h"
|
|
|
|
#ifdef HAVE_LIBTINYDTLS
|
|
|
|
/* We want TinyDTLS versions of these, not libcoap versions */
|
|
#undef PACKAGE_BUGREPORT
|
|
#undef PACKAGE_NAME
|
|
#undef PACKAGE_STRING
|
|
#undef PACKAGE_TARNAME
|
|
#undef PACKAGE_URL
|
|
#undef PACKAGE_VERSION
|
|
|
|
#include <tinydtls/tinydtls.h>
|
|
#include <tinydtls/dtls.h>
|
|
#include <tinydtls/dtls_debug.h>
|
|
|
|
typedef struct coap_tiny_context_t {
|
|
struct dtls_context_t *dtls_context;
|
|
coap_context_t *coap_context;
|
|
#ifdef DTLS_ECC
|
|
coap_dtls_pki_t setup_data;
|
|
coap_binary_t *priv_key;
|
|
coap_binary_t *pub_key;
|
|
#endif /* DTLS_ECC */
|
|
} coap_tiny_context_t;
|
|
|
|
static dtls_tick_t dtls_tick_0 = 0;
|
|
static coap_tick_t coap_tick_0 = 0;
|
|
|
|
int
|
|
coap_dtls_is_supported(void) {
|
|
return 1;
|
|
}
|
|
|
|
void coap_dtls_startup(void) {
|
|
dtls_init();
|
|
dtls_ticks(&dtls_tick_0);
|
|
coap_ticks(&coap_tick_0);
|
|
}
|
|
|
|
void coap_dtls_shutdown(void) {
|
|
}
|
|
|
|
void *
|
|
coap_dtls_get_tls(const coap_session_t *c_session,
|
|
coap_tls_library_t *tls_lib) {
|
|
if (tls_lib)
|
|
*tls_lib = COAP_TLS_LIBRARY_TINYDTLS;
|
|
if (c_session && c_session->context && c_session->context->dtls_context) {
|
|
const coap_tiny_context_t *t_context =
|
|
(const coap_tiny_context_t *)c_session->context->dtls_context;
|
|
|
|
return t_context->dtls_context;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
coap_dtls_set_log_level(int level) {
|
|
dtls_set_log_level(level);
|
|
}
|
|
|
|
int
|
|
coap_dtls_get_log_level(void) {
|
|
return dtls_get_log_level();
|
|
}
|
|
|
|
static void get_session_addr(const session_t *s, coap_address_t *a) {
|
|
#ifdef WITH_CONTIKI
|
|
a->addr = s->addr;
|
|
a->port = s->port;
|
|
#else
|
|
if (s->addr.sa.sa_family == AF_INET6) {
|
|
a->size = (socklen_t)sizeof(a->addr.sin6);
|
|
a->addr.sin6 = s->addr.sin6;
|
|
} else if (s->addr.sa.sa_family == AF_INET) {
|
|
a->size = (socklen_t)sizeof(a->addr.sin);
|
|
a->addr.sin = s->addr.sin;
|
|
} else {
|
|
a->size = (socklen_t)s->size;
|
|
a->addr.sa = s->addr.sa;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void put_session_addr(const coap_address_t *a, session_t *s) {
|
|
#ifdef WITH_CONTIKI
|
|
s->size = (unsigned char)sizeof(s->addr);
|
|
s->addr = a->addr;
|
|
s->port = a->port;
|
|
#else
|
|
if (a->addr.sa.sa_family == AF_INET6) {
|
|
s->size = (socklen_t)sizeof(s->addr.sin6);
|
|
s->addr.sin6 = a->addr.sin6;
|
|
} else if (a->addr.sa.sa_family == AF_INET) {
|
|
s->size = (socklen_t)sizeof(s->addr.sin);
|
|
s->addr.sin = a->addr.sin;
|
|
} else {
|
|
s->size = (socklen_t)a->size;
|
|
s->addr.sa = a->addr.sa;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
dtls_send_to_peer(struct dtls_context_t *dtls_context,
|
|
session_t *dtls_session, uint8 *data, size_t len) {
|
|
coap_tiny_context_t *t_context =
|
|
(coap_tiny_context_t *)dtls_get_app_data(dtls_context);
|
|
coap_context_t *coap_context = t_context ? t_context->coap_context : NULL;
|
|
coap_session_t *coap_session;
|
|
coap_address_t remote_addr;
|
|
|
|
assert(coap_context);
|
|
get_session_addr(dtls_session, &remote_addr);
|
|
coap_session = coap_session_get_by_peer(coap_context, &remote_addr, dtls_session->ifindex);
|
|
if (!coap_session) {
|
|
coap_log(LOG_WARNING, "dtls_send_to_peer: cannot find local interface\n");
|
|
return -3;
|
|
}
|
|
return (int)coap_session_send(coap_session, data, len);
|
|
}
|
|
|
|
static int
|
|
dtls_application_data(struct dtls_context_t *dtls_context,
|
|
session_t *dtls_session, uint8 *data, size_t len) {
|
|
coap_tiny_context_t *t_context =
|
|
(coap_tiny_context_t *)dtls_get_app_data(dtls_context);
|
|
coap_context_t *coap_context = t_context ? t_context->coap_context : NULL;
|
|
coap_session_t *coap_session;
|
|
coap_address_t remote_addr;
|
|
|
|
assert(coap_context);
|
|
get_session_addr(dtls_session, &remote_addr);
|
|
coap_session = coap_session_get_by_peer(coap_context, &remote_addr, dtls_session->ifindex);
|
|
if (!coap_session) {
|
|
coap_log(LOG_DEBUG,
|
|
"dropped message that was received on invalid interface\n");
|
|
return -1;
|
|
}
|
|
|
|
return coap_handle_dgram(coap_context, coap_session, data, len);
|
|
}
|
|
|
|
static int coap_event_dtls = 0;
|
|
|
|
static int
|
|
dtls_event(struct dtls_context_t *dtls_context,
|
|
session_t *dtls_session,
|
|
dtls_alert_level_t level,
|
|
uint16_t code) {
|
|
(void)dtls_context;
|
|
(void)dtls_session;
|
|
|
|
if (level == DTLS_ALERT_LEVEL_FATAL)
|
|
coap_event_dtls = COAP_EVENT_DTLS_ERROR;
|
|
|
|
/* handle DTLS events */
|
|
switch (code) {
|
|
case DTLS_ALERT_CLOSE_NOTIFY:
|
|
{
|
|
coap_event_dtls = COAP_EVENT_DTLS_CLOSED;
|
|
break;
|
|
}
|
|
case DTLS_EVENT_CONNECTED:
|
|
{
|
|
coap_event_dtls = COAP_EVENT_DTLS_CONNECTED;
|
|
break;
|
|
}
|
|
case DTLS_EVENT_RENEGOTIATE:
|
|
{
|
|
coap_event_dtls = COAP_EVENT_DTLS_RENEGOTIATE;
|
|
break;
|
|
}
|
|
default:
|
|
;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This function is the "key store" for tinyDTLS. It is called to
|
|
* retrieve a key for the given identity within this particular
|
|
* session. */
|
|
static int
|
|
get_psk_info(struct dtls_context_t *dtls_context,
|
|
const session_t *dtls_session,
|
|
dtls_credentials_type_t type,
|
|
const uint8_t *id, size_t id_len,
|
|
unsigned char *result, size_t result_length) {
|
|
|
|
coap_tiny_context_t *t_context =
|
|
(coap_tiny_context_t *)dtls_get_app_data(dtls_context);
|
|
coap_context_t *coap_context = t_context ? t_context->coap_context : NULL;
|
|
coap_session_t *coap_session;
|
|
int fatal_error = DTLS_ALERT_INTERNAL_ERROR;
|
|
coap_address_t remote_addr;
|
|
#if COAP_CLIENT_SUPPORT
|
|
coap_dtls_cpsk_t *setup_cdata;
|
|
const coap_bin_const_t *psk_identity;
|
|
const coap_dtls_cpsk_info_t *cpsk_info;
|
|
#endif /* COAP_CLIENT_SUPPORT */
|
|
const coap_bin_const_t *psk_key;
|
|
#if COAP_SERVER_SUPPORT
|
|
coap_dtls_spsk_t *setup_sdata;
|
|
const coap_bin_const_t *psk_hint;
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
assert(coap_context);
|
|
get_session_addr(dtls_session, &remote_addr);
|
|
coap_session = coap_session_get_by_peer(coap_context, &remote_addr, dtls_session->ifindex);
|
|
if (!coap_session) {
|
|
coap_log(LOG_DEBUG, "cannot get PSK, session not found\n");
|
|
goto error;
|
|
}
|
|
|
|
switch (type) {
|
|
case DTLS_PSK_IDENTITY:
|
|
|
|
#if COAP_CLIENT_SUPPORT
|
|
if (coap_session->type != COAP_SESSION_TYPE_CLIENT)
|
|
goto error;
|
|
|
|
setup_cdata = &coap_session->cpsk_setup_data;
|
|
|
|
coap_bin_const_t temp;
|
|
temp.s = id;
|
|
temp.length = id_len;
|
|
coap_session_refresh_psk_hint(coap_session, &temp);
|
|
|
|
coap_log(LOG_DEBUG, "got psk_identity_hint: '%.*s'\n", (int)id_len,
|
|
id ? (const char*)id : "");
|
|
|
|
if (setup_cdata->validate_ih_call_back) {
|
|
coap_str_const_t lhint;
|
|
|
|
lhint.length = id_len;
|
|
lhint.s = id;
|
|
cpsk_info =
|
|
setup_cdata->validate_ih_call_back(&lhint,
|
|
coap_session,
|
|
setup_cdata->ih_call_back_arg);
|
|
if (cpsk_info) {
|
|
psk_identity = &cpsk_info->identity;
|
|
coap_session_refresh_psk_identity(coap_session, &cpsk_info->identity);
|
|
coap_session_refresh_psk_key(coap_session, &cpsk_info->key);
|
|
}
|
|
else {
|
|
psk_identity = NULL;
|
|
}
|
|
}
|
|
else {
|
|
psk_identity = coap_get_session_client_psk_identity(coap_session);
|
|
}
|
|
if (psk_identity == NULL) {
|
|
coap_log(LOG_WARNING, "no PSK identity given\n");
|
|
fatal_error = DTLS_ALERT_CLOSE_NOTIFY;
|
|
goto error;
|
|
}
|
|
if (psk_identity->length > result_length) {
|
|
coap_log(LOG_WARNING,
|
|
"psk_identity too large, truncated to %zd bytes\n",
|
|
result_length);
|
|
}
|
|
else {
|
|
/* Reduce to match */
|
|
result_length = psk_identity->length;
|
|
}
|
|
memcpy(result, psk_identity->s, result_length);
|
|
return result_length;
|
|
#else /* ! COAP_CLIENT_SUPPORT */
|
|
return 0;
|
|
#endif /* ! COAP_CLIENT_SUPPORT */
|
|
|
|
case DTLS_PSK_KEY:
|
|
#if COAP_CLIENT_SUPPORT
|
|
if (coap_session->type == COAP_SESSION_TYPE_CLIENT) {
|
|
psk_key = coap_get_session_client_psk_key(coap_session);
|
|
if (psk_key == NULL) {
|
|
coap_log(LOG_WARNING, "no PSK key given\n");
|
|
fatal_error = DTLS_ALERT_CLOSE_NOTIFY;
|
|
goto error;
|
|
}
|
|
if (psk_key->length > result_length) {
|
|
coap_log(LOG_WARNING,
|
|
"psk_key too large, truncated to %zd bytes\n",
|
|
result_length);
|
|
}
|
|
else {
|
|
/* Reduce to match */
|
|
result_length = psk_key->length;
|
|
}
|
|
memcpy(result, psk_key->s, result_length);
|
|
return result_length;
|
|
}
|
|
#endif /* COAP_CLIENT_SUPPORT */
|
|
#if COAP_SERVER_SUPPORT
|
|
if (coap_session->type != COAP_SESSION_TYPE_CLIENT) {
|
|
coap_bin_const_t lidentity;
|
|
|
|
lidentity.length = id ? id_len : 0;
|
|
lidentity.s = id ? (const uint8_t*)id : (const uint8_t *)"";
|
|
setup_sdata = &coap_session->context->spsk_setup_data;
|
|
|
|
/* Track the Identity being used */
|
|
coap_session_refresh_psk_identity(coap_session, &lidentity);
|
|
|
|
coap_log(LOG_DEBUG, "got psk_identity: '%.*s'\n",
|
|
(int)lidentity.length, lidentity.s);
|
|
|
|
if (setup_sdata->validate_id_call_back) {
|
|
psk_key =
|
|
setup_sdata->validate_id_call_back(&lidentity,
|
|
coap_session,
|
|
setup_sdata->id_call_back_arg);
|
|
}
|
|
else {
|
|
psk_key = coap_get_session_server_psk_key(coap_session);
|
|
}
|
|
|
|
if (psk_key == NULL) {
|
|
coap_log(LOG_WARNING, "no PSK key given\n");
|
|
return 0;
|
|
}
|
|
if (setup_sdata->validate_id_call_back)
|
|
coap_session_refresh_psk_key(coap_session, psk_key);
|
|
if (psk_key->length > result_length) {
|
|
coap_log(LOG_WARNING,
|
|
"psk_key too large, truncated to %zd bytes\n",
|
|
result_length);
|
|
}
|
|
else {
|
|
/* Reduce to match */
|
|
result_length = psk_key->length;
|
|
}
|
|
memcpy(result, psk_key->s, result_length);
|
|
return result_length;
|
|
}
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
return 0;
|
|
|
|
case DTLS_PSK_HINT:
|
|
#if COAP_SERVER_SUPPORT
|
|
psk_hint = coap_get_session_server_psk_hint(coap_session);
|
|
if (psk_hint == NULL)
|
|
return 0;
|
|
if (psk_hint->length > result_length) {
|
|
coap_log(LOG_WARNING,
|
|
"psk_hint too large, truncated to %zd bytes\n",
|
|
result_length);
|
|
}
|
|
else {
|
|
/* Reduce to match */
|
|
result_length = psk_hint->length;
|
|
}
|
|
memcpy(result, psk_hint->s, result_length);
|
|
return result_length;
|
|
#else /* COAP_SERVER_SUPPORT */
|
|
return 0;
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
default:
|
|
coap_log(LOG_WARNING, "unsupported request type: %d\n", type);
|
|
}
|
|
|
|
error:
|
|
return dtls_alert_fatal_create(fatal_error);
|
|
}
|
|
|
|
#ifdef DTLS_ECC
|
|
static int
|
|
get_ecdsa_key(struct dtls_context_t *dtls_context,
|
|
const session_t *dtls_session COAP_UNUSED,
|
|
const dtls_ecdsa_key_t **result) {
|
|
static dtls_ecdsa_key_t ecdsa_key;
|
|
coap_tiny_context_t *t_context =
|
|
(coap_tiny_context_t *)dtls_get_app_data(dtls_context);
|
|
|
|
ecdsa_key.curve = DTLS_ECDH_CURVE_SECP256R1;
|
|
ecdsa_key.priv_key = t_context->priv_key->s;
|
|
ecdsa_key.pub_key_x = t_context->pub_key->s;
|
|
ecdsa_key.pub_key_y = &t_context->pub_key->s[DTLS_EC_KEY_SIZE];
|
|
|
|
*result = &ecdsa_key;
|
|
return 0;
|
|
}
|
|
|
|
/* first part of Raw public key, the is the start of the Subject Public Key */
|
|
static const unsigned char cert_asn1_header[] = {
|
|
0x30, 0x59, /* SEQUENCE, length 89 bytes */
|
|
0x30, 0x13, /* SEQUENCE, length 19 bytes */
|
|
0x06, 0x07, /* OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) */
|
|
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
|
0x06, 0x08, /* OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) */
|
|
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
|
0x03, 0x42, 0x00, /* BIT STRING, length 66 bytes, 0 bits unused */
|
|
0x04 /* uncompressed, followed by the r and s values of the public key */
|
|
};
|
|
#define DTLS_CE_LENGTH (sizeof(cert_asn1_header) + key_size + key_size)
|
|
|
|
static int
|
|
verify_ecdsa_key(struct dtls_context_t *dtls_context COAP_UNUSED,
|
|
const session_t *dtls_session COAP_UNUSED,
|
|
const uint8_t *other_pub_x,
|
|
const uint8_t *other_pub_y,
|
|
size_t key_size) {
|
|
coap_tiny_context_t *t_context =
|
|
(coap_tiny_context_t *)dtls_get_app_data(dtls_context);
|
|
if (t_context && t_context->setup_data.validate_cn_call_back) {
|
|
/* Need to build asn.1 certificate - code taken from tinydtls */
|
|
uint8 *p;
|
|
uint8 buf[DTLS_CE_LENGTH];
|
|
coap_session_t *c_session;
|
|
coap_address_t remote_addr;
|
|
|
|
/* Certificate
|
|
*
|
|
* Start message construction at beginning of buffer. */
|
|
p = buf;
|
|
|
|
memcpy(p, &cert_asn1_header, sizeof(cert_asn1_header));
|
|
p += sizeof(cert_asn1_header);
|
|
|
|
memcpy(p, other_pub_x, key_size);
|
|
p += key_size;
|
|
|
|
memcpy(p, other_pub_y, key_size);
|
|
p += key_size;
|
|
|
|
assert(p <= (buf + sizeof(buf)));
|
|
|
|
get_session_addr(dtls_session, &remote_addr);
|
|
c_session = coap_session_get_by_peer(t_context->coap_context,
|
|
&remote_addr, dtls_session->ifindex);
|
|
if (!c_session)
|
|
return -3;
|
|
if (!t_context->setup_data.validate_cn_call_back(COAP_DTLS_RPK_CERT_CN,
|
|
buf, p-buf, c_session, 0, 1, t_context->setup_data.cn_call_back_arg)) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static dtls_handler_t ec_cb = {
|
|
.write = dtls_send_to_peer,
|
|
.read = dtls_application_data,
|
|
.event = dtls_event,
|
|
.get_psk_info = NULL,
|
|
.get_ecdsa_key = get_ecdsa_key,
|
|
.verify_ecdsa_key = verify_ecdsa_key
|
|
};
|
|
#endif /* DTLS_ECC */
|
|
|
|
static dtls_handler_t psk_cb = {
|
|
.write = dtls_send_to_peer,
|
|
.read = dtls_application_data,
|
|
.event = dtls_event,
|
|
.get_psk_info = get_psk_info,
|
|
#ifdef DTLS_ECC
|
|
.get_ecdsa_key = NULL,
|
|
.verify_ecdsa_key = NULL
|
|
#endif
|
|
};
|
|
|
|
void *
|
|
coap_dtls_new_context(coap_context_t *coap_context) {
|
|
coap_tiny_context_t *t_context = coap_malloc(sizeof(coap_tiny_context_t));
|
|
struct dtls_context_t *dtls_context = t_context ? dtls_new_context(t_context) : NULL;
|
|
if (!dtls_context)
|
|
goto error;
|
|
memset(t_context, 0, sizeof(coap_tiny_context_t));
|
|
t_context->coap_context = coap_context;
|
|
t_context->dtls_context = dtls_context;
|
|
dtls_set_handler(dtls_context, &psk_cb);
|
|
return t_context;
|
|
error:
|
|
if (t_context)
|
|
coap_free(t_context);
|
|
if (dtls_context)
|
|
coap_dtls_free_context(dtls_context);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
coap_dtls_free_context(void *handle) {
|
|
if (handle) {
|
|
coap_tiny_context_t *t_context = (coap_tiny_context_t *)handle;
|
|
#ifdef DTLS_ECC
|
|
if (t_context->priv_key) {
|
|
coap_delete_binary(t_context->priv_key);
|
|
t_context->priv_key = NULL;
|
|
}
|
|
if (t_context->pub_key) {
|
|
coap_delete_binary(t_context->pub_key);
|
|
t_context->pub_key = NULL;
|
|
}
|
|
#endif /* DTLS_ECC */
|
|
if (t_context->dtls_context)
|
|
dtls_free_context(t_context->dtls_context);
|
|
coap_free(t_context);
|
|
}
|
|
}
|
|
|
|
static session_t *
|
|
coap_dtls_new_session(coap_session_t *session) {
|
|
session_t *dtls_session = coap_malloc_type(COAP_DTLS_SESSION, sizeof(session_t));
|
|
|
|
if (dtls_session) {
|
|
/* create tinydtls session object from remote address and local
|
|
* endpoint handle */
|
|
dtls_session_init(dtls_session);
|
|
put_session_addr(&session->addr_info.remote, dtls_session);
|
|
dtls_session->ifindex = session->ifindex;
|
|
coap_log(LOG_DEBUG, "***new session %p\n", (void *)dtls_session);
|
|
}
|
|
|
|
return dtls_session;
|
|
}
|
|
|
|
#if COAP_SERVER_SUPPORT
|
|
void *coap_dtls_new_server_session(coap_session_t *session) {
|
|
return coap_dtls_new_session(session);
|
|
}
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
#if COAP_CLIENT_SUPPORT
|
|
void *coap_dtls_new_client_session(coap_session_t *session) {
|
|
dtls_peer_t *peer;
|
|
coap_tiny_context_t *t_context = (coap_tiny_context_t *)session->context->dtls_context;
|
|
dtls_context_t *dtls_context = t_context ? t_context->dtls_context : NULL;
|
|
session_t *dtls_session = dtls_context ? coap_dtls_new_session(session) : NULL;
|
|
|
|
if (!dtls_session)
|
|
return NULL;
|
|
peer =
|
|
dtls_get_peer(dtls_context, dtls_session);
|
|
|
|
if (!peer) {
|
|
/* The peer connection does not yet exist. */
|
|
/* dtls_connect() returns a value greater than zero if a new
|
|
* connection attempt is made, 0 for session reuse. */
|
|
if (dtls_connect(dtls_context, dtls_session) >= 0) {
|
|
peer =
|
|
dtls_get_peer(dtls_context, dtls_session);
|
|
}
|
|
}
|
|
|
|
if (!peer) {
|
|
/* delete existing session because the peer object has been invalidated */
|
|
coap_free_type(COAP_DTLS_SESSION, dtls_session);
|
|
dtls_session = NULL;
|
|
}
|
|
|
|
return dtls_session;
|
|
}
|
|
#endif /* COAP_CLIENT_SUPPORT */
|
|
|
|
void
|
|
coap_dtls_session_update_mtu(coap_session_t *session) {
|
|
(void)session;
|
|
}
|
|
|
|
void
|
|
coap_dtls_free_session(coap_session_t *coap_session) {
|
|
coap_tiny_context_t *t_context =
|
|
(coap_tiny_context_t *)coap_session->context->dtls_context;
|
|
dtls_context_t *dtls_context = t_context ? t_context->dtls_context : NULL;
|
|
|
|
if (dtls_context == NULL)
|
|
return;
|
|
if (coap_session->tls && dtls_context) {
|
|
dtls_peer_t *peer = dtls_get_peer(dtls_context, (session_t *)coap_session->tls);
|
|
if ( peer )
|
|
dtls_reset_peer(dtls_context, peer);
|
|
else
|
|
dtls_close(dtls_context, (session_t *)coap_session->tls);
|
|
coap_log(LOG_DEBUG, "***removed session %p\n", coap_session->tls);
|
|
coap_free_type(COAP_DTLS_SESSION, coap_session->tls);
|
|
coap_session->tls = NULL;
|
|
coap_handle_event(coap_session->context, COAP_EVENT_DTLS_CLOSED, coap_session);
|
|
}
|
|
}
|
|
|
|
int
|
|
coap_dtls_send(coap_session_t *session,
|
|
const uint8_t *data,
|
|
size_t data_len
|
|
) {
|
|
int res;
|
|
uint8_t *data_rw;
|
|
coap_tiny_context_t *t_context = (coap_tiny_context_t *)session->context->dtls_context;
|
|
dtls_context_t *dtls_context = t_context ? t_context->dtls_context : NULL;
|
|
|
|
assert(dtls_context);
|
|
coap_log(LOG_DEBUG, "call dtls_write\n");
|
|
|
|
coap_event_dtls = -1;
|
|
/* Need to do this to not get a compiler warning about const parameters */
|
|
memcpy (&data_rw, &data, sizeof(data_rw));
|
|
res = dtls_write(dtls_context,
|
|
(session_t *)session->tls, data_rw, data_len);
|
|
|
|
if (res < 0)
|
|
coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n");
|
|
|
|
if (coap_event_dtls >= 0) {
|
|
/* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
|
|
if (coap_event_dtls != COAP_EVENT_DTLS_CLOSED)
|
|
coap_handle_event(session->context, coap_event_dtls, session);
|
|
if (coap_event_dtls == COAP_EVENT_DTLS_CONNECTED)
|
|
coap_session_connected(session);
|
|
else if (coap_event_dtls == COAP_EVENT_DTLS_CLOSED || coap_event_dtls == COAP_EVENT_DTLS_ERROR)
|
|
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int coap_dtls_is_context_timeout(void) {
|
|
return 1;
|
|
}
|
|
|
|
coap_tick_t coap_dtls_get_context_timeout(void *tiny_context) {
|
|
clock_time_t next = 0;
|
|
coap_tiny_context_t *t_context = (coap_tiny_context_t *)tiny_context;
|
|
dtls_context_t *dtls_context = t_context ? t_context->dtls_context : NULL;
|
|
if (tiny_context)
|
|
dtls_check_retransmit(dtls_context, &next);
|
|
if (next > 0)
|
|
return ((coap_tick_t)(next - dtls_tick_0)) * COAP_TICKS_PER_SECOND / DTLS_TICKS_PER_SECOND + coap_tick_0;
|
|
return 0;
|
|
}
|
|
|
|
coap_tick_t coap_dtls_get_timeout(coap_session_t *session, coap_tick_t now) {
|
|
(void)session;
|
|
(void)now;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* return 1 timed out
|
|
* 0 still timing out
|
|
*/
|
|
int
|
|
coap_dtls_handle_timeout(coap_session_t *session) {
|
|
(void)session;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
coap_dtls_receive(coap_session_t *session,
|
|
const uint8_t *data,
|
|
size_t data_len
|
|
) {
|
|
session_t *dtls_session = (session_t *)session->tls;
|
|
int err;
|
|
uint8_t *data_rw;
|
|
coap_tiny_context_t *t_context = (coap_tiny_context_t *)session->context->dtls_context;
|
|
dtls_context_t *dtls_context = t_context ? t_context->dtls_context : NULL;
|
|
|
|
assert(dtls_context);
|
|
coap_event_dtls = -1;
|
|
/* Need to do this to not get a compiler warning about const parameters */
|
|
memcpy (&data_rw, &data, sizeof(data_rw));
|
|
err = dtls_handle_message(dtls_context, dtls_session, data_rw, (int)data_len);
|
|
|
|
if (err){
|
|
coap_event_dtls = COAP_EVENT_DTLS_ERROR;
|
|
}
|
|
|
|
if (coap_event_dtls >= 0) {
|
|
/* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
|
|
if (coap_event_dtls != COAP_EVENT_DTLS_CLOSED)
|
|
coap_handle_event(session->context, coap_event_dtls, session);
|
|
if (coap_event_dtls == COAP_EVENT_DTLS_CONNECTED)
|
|
coap_session_connected(session);
|
|
else if (coap_event_dtls == COAP_EVENT_DTLS_CLOSED || coap_event_dtls == COAP_EVENT_DTLS_ERROR)
|
|
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#if COAP_SERVER_SUPPORT
|
|
int
|
|
coap_dtls_hello(coap_session_t *session,
|
|
const uint8_t *data,
|
|
size_t data_len
|
|
) {
|
|
session_t dtls_session;
|
|
coap_tiny_context_t *t_context = (coap_tiny_context_t *)session->context->dtls_context;
|
|
dtls_context_t *dtls_context = t_context ? t_context->dtls_context : NULL;
|
|
uint8_t *data_rw;
|
|
|
|
assert(dtls_context);
|
|
dtls_session_init(&dtls_session);
|
|
put_session_addr(&session->addr_info.remote, &dtls_session);
|
|
dtls_session.ifindex = session->ifindex;
|
|
/* Need to do this to not get a compiler warning about const parameters */
|
|
memcpy (&data_rw, &data, sizeof(data_rw));
|
|
int res = dtls_handle_message(dtls_context, &dtls_session,
|
|
data_rw, (int)data_len);
|
|
if (res >= 0) {
|
|
if (dtls_get_peer(dtls_context, &dtls_session))
|
|
res = 1;
|
|
else
|
|
res = 0;
|
|
}
|
|
return res;
|
|
}
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
unsigned int coap_dtls_get_overhead(coap_session_t *session) {
|
|
(void)session;
|
|
return 13 + 8 + 8;
|
|
}
|
|
|
|
int coap_tls_is_supported(void) {
|
|
return 0;
|
|
}
|
|
|
|
coap_tls_version_t *
|
|
coap_get_tls_library_version(void) {
|
|
static coap_tls_version_t version;
|
|
const char *vers = dtls_package_version();
|
|
|
|
version.version = 0;
|
|
if (vers) {
|
|
long int p1, p2 = 0, p3 = 0;
|
|
char* endptr;
|
|
|
|
p1 = strtol(vers, &endptr, 10);
|
|
if (*endptr == '.') {
|
|
p2 = strtol(endptr+1, &endptr, 10);
|
|
if (*endptr == '.') {
|
|
p3 = strtol(endptr+1, &endptr, 10);
|
|
}
|
|
}
|
|
version.version = (p1 << 16) | (p2 << 8) | p3;
|
|
}
|
|
version.built_version = version.version;
|
|
version.type = COAP_TLS_LIBRARY_TINYDTLS;
|
|
return &version;
|
|
}
|
|
|
|
#ifdef DTLS_ECC
|
|
static const uint8_t b64_6[256] =
|
|
{
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
/* + / */
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
|
/* 0 1 2 3 4 5 6 7 8 9 = */
|
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
|
|
/* A B C D E F G H I J K L M N O */
|
|
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
/* P Q R S T U V W X Y Z */
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
|
|
/* a b c d e f g h i j k l m n o */
|
|
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
|
/* p q r s t u v w x y z */
|
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
|
|
};
|
|
|
|
/* caller must free off returned coap_binary_t* */
|
|
static coap_binary_t *
|
|
pem_base64_decode (const uint8_t *data, size_t size)
|
|
{
|
|
uint8_t *tbuf = coap_malloc(size);
|
|
size_t nbytesdecoded;
|
|
size_t i;
|
|
coap_binary_t *decoded;
|
|
uint8_t *ptr;
|
|
uint8_t *out;
|
|
size_t nb64bytes = 0;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
switch (data[i]) {
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t':
|
|
break;
|
|
default:
|
|
if (b64_6[data[i]] == 64)
|
|
goto end;
|
|
tbuf[nb64bytes++] = data[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
end:
|
|
nbytesdecoded = ((nb64bytes + 3) / 4) * 3;
|
|
decoded = coap_new_binary(nbytesdecoded + 1);
|
|
if (!decoded)
|
|
return NULL;
|
|
|
|
out = decoded->s;
|
|
ptr = tbuf;
|
|
|
|
while (nb64bytes > 4) {
|
|
*(out++) = b64_6[ptr[0]] << 2 | b64_6[ptr[1]] >> 4;
|
|
*(out++) = b64_6[ptr[1]] << 4 | b64_6[ptr[2]] >> 2;
|
|
*(out++) = b64_6[ptr[2]] << 6 | b64_6[ptr[3]];
|
|
ptr += 4;
|
|
nb64bytes -= 4;
|
|
}
|
|
|
|
/* Note: (nb64bytes == 1) is an error */
|
|
if (nb64bytes > 1) {
|
|
*(out++) = b64_6[ptr[0]] << 2 | b64_6[ptr[1]] >> 4;
|
|
}
|
|
if (nb64bytes > 2) {
|
|
*(out++) = b64_6[ptr[1]] << 4 | b64_6[ptr[2]] >> 2;
|
|
}
|
|
if (nb64bytes > 3) {
|
|
*(out++) = b64_6[ptr[2]] << 6 | b64_6[ptr[3]];
|
|
}
|
|
|
|
decoded->length = nbytesdecoded - ((4 - nb64bytes) & 3);
|
|
coap_free(tbuf);
|
|
return decoded;
|
|
}
|
|
|
|
typedef coap_binary_t * (*asn1_callback)(const uint8_t *data, size_t size);
|
|
|
|
static int
|
|
asn1_verify_privkey(const uint8_t *data, size_t size)
|
|
{
|
|
/* Check if we have the private key (with optional leading 0x00) */
|
|
/* skip leading 0x00 */
|
|
if (size - 1 == DTLS_EC_KEY_SIZE && *data == '\000') {
|
|
--size;
|
|
++data;
|
|
}
|
|
|
|
/* Check if we have the private key */
|
|
if (size != DTLS_EC_KEY_SIZE)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
asn1_verify_pubkey(const uint8_t *data, size_t size)
|
|
{
|
|
(void)data;
|
|
|
|
/* We have the public key
|
|
(with a leading 0x00 (no unused bits) 0x04 (not compressed() */
|
|
if (size - 2 != 2 * DTLS_EC_KEY_SIZE)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
asn1_verify_curve(const uint8_t *data, size_t size)
|
|
{
|
|
static uint8_t prime256v1_oid[] =
|
|
/* OID 1.2.840.10045.3.1.7 */
|
|
{ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 };
|
|
|
|
/* Check that we have the correct EC (only one supported) */
|
|
if (size != sizeof(prime256v1_oid) ||
|
|
memcmp(data, prime256v1_oid, size) != 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
asn1_verify_pkcs8_version(const uint8_t *data, size_t size)
|
|
{
|
|
/* Check that we have the version */
|
|
if (size != 1 || *data != 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
asn1_verify_ec_identifier(const uint8_t *data, size_t size)
|
|
{
|
|
static uint8_t ec_public_key_oid[] =
|
|
/* OID 1.2.840.10045.2.1 */
|
|
{ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 };
|
|
|
|
/* Check that we have the correct ecPublicKey */
|
|
if (size != sizeof(ec_public_key_oid) ||
|
|
memcmp(data, ec_public_key_oid, size) != 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
asn1_verify_ec_key(const uint8_t *data, size_t size)
|
|
{
|
|
(void)data;
|
|
|
|
if (size == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
asn1_derive_keys(coap_tiny_context_t *t_context,
|
|
const uint8_t *priv_data, size_t priv_len,
|
|
const uint8_t *pub_data, size_t pub_len,
|
|
int is_pkcs8)
|
|
{
|
|
coap_binary_t *test;
|
|
|
|
t_context->priv_key = get_asn1_tag(COAP_ASN1_OCTETSTRING, priv_data,
|
|
priv_len, asn1_verify_privkey);
|
|
if (!t_context->priv_key) {
|
|
coap_log(LOG_INFO, "EC Private Key (RPK) invalid\n");
|
|
return 0;
|
|
}
|
|
/* skip leading 0x00 */
|
|
if (t_context->priv_key->length - 1 == DTLS_EC_KEY_SIZE &&
|
|
t_context->priv_key->s[0] == '\000') {
|
|
t_context->priv_key->length--;
|
|
t_context->priv_key->s++;
|
|
}
|
|
|
|
if (!is_pkcs8) {
|
|
/* pkcs8 abstraction tested for valid eliptic curve */
|
|
test = get_asn1_tag(COAP_ASN1_IDENTIFIER, priv_data, priv_len,
|
|
asn1_verify_curve);
|
|
if (!test) {
|
|
coap_log(LOG_INFO, "EC Private Key (RPK) invalid elliptic curve\n");
|
|
coap_delete_binary(t_context->priv_key);
|
|
t_context->priv_key = NULL;
|
|
return 0;
|
|
}
|
|
coap_delete_binary(test);
|
|
}
|
|
|
|
t_context->pub_key = get_asn1_tag(COAP_ASN1_BITSTRING, pub_data, pub_len,
|
|
asn1_verify_pubkey);
|
|
if (!t_context->pub_key) {
|
|
coap_log(LOG_INFO, "EC Public Key (RPK) invalid\n");
|
|
coap_delete_binary(t_context->priv_key);
|
|
t_context->priv_key = NULL;
|
|
return 0;
|
|
}
|
|
/* Drop leading 0x00 and 0x04 */
|
|
t_context->pub_key->s += 2;
|
|
t_context->pub_key->length -= 2;
|
|
dtls_set_handler(t_context->dtls_context, &ec_cb);
|
|
return 1;
|
|
}
|
|
|
|
static coap_binary_t *
|
|
ec_abstract_pkcs8_asn1(const uint8_t *asn1_ptr, size_t asn1_length)
|
|
{
|
|
coap_binary_t *test;
|
|
|
|
test = get_asn1_tag(COAP_ASN1_INTEGER, asn1_ptr, asn1_length,
|
|
asn1_verify_pkcs8_version);
|
|
if (!test)
|
|
return 0;
|
|
|
|
coap_delete_binary(test);
|
|
|
|
test = get_asn1_tag(COAP_ASN1_IDENTIFIER, asn1_ptr, asn1_length,
|
|
asn1_verify_ec_identifier);
|
|
if (!test)
|
|
return 0;
|
|
coap_delete_binary(test);
|
|
|
|
test = get_asn1_tag(COAP_ASN1_IDENTIFIER, asn1_ptr, asn1_length,
|
|
asn1_verify_curve);
|
|
if (!test) {
|
|
coap_log(LOG_INFO, "EC Private Key (RPK) invalid elliptic curve\n");
|
|
return 0;
|
|
}
|
|
coap_delete_binary(test);
|
|
|
|
test = get_asn1_tag(COAP_ASN1_OCTETSTRING, asn1_ptr, asn1_length,
|
|
asn1_verify_ec_key);
|
|
return test;
|
|
}
|
|
|
|
static coap_binary_t *
|
|
pem_decode_mem_asn1(const char *begstr, const uint8_t *str)
|
|
{
|
|
char *bcp = str ? strstr((const char*)str, begstr) : NULL;
|
|
char *tcp = bcp ? strstr(bcp, "-----END ") : NULL;
|
|
|
|
if (bcp && tcp) {
|
|
bcp += strlen(begstr);
|
|
return pem_base64_decode ((const uint8_t *)bcp, tcp - bcp);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* DTLS_ECC */
|
|
|
|
int
|
|
coap_dtls_context_set_pki(coap_context_t *ctx,
|
|
const coap_dtls_pki_t* setup_data,
|
|
const coap_dtls_role_t role COAP_UNUSED
|
|
) {
|
|
#ifdef DTLS_ECC
|
|
coap_tiny_context_t *t_context;
|
|
coap_binary_t *asn1_priv;
|
|
coap_binary_t *asn1_pub;
|
|
coap_binary_t *asn1_temp;
|
|
int is_pkcs8 = 0;
|
|
|
|
if (!setup_data)
|
|
return 0;
|
|
if (setup_data->version != COAP_DTLS_PKI_SETUP_VERSION)
|
|
return 0;
|
|
if (!setup_data->is_rpk_not_cert) {
|
|
coap_log(LOG_WARNING, "Only RPK, not full PKI is supported\n");
|
|
return 0;
|
|
}
|
|
if (!ctx)
|
|
return 0;
|
|
t_context = (coap_tiny_context_t *)ctx->dtls_context;
|
|
if (!t_context)
|
|
return 0;
|
|
if (t_context->priv_key) {
|
|
coap_delete_binary(t_context->priv_key);
|
|
t_context->priv_key = NULL;
|
|
}
|
|
if (t_context->pub_key) {
|
|
coap_delete_binary(t_context->pub_key);
|
|
t_context->pub_key = NULL;
|
|
}
|
|
t_context->setup_data = *setup_data;
|
|
|
|
/* All should be RPK only now */
|
|
switch (setup_data->pki_key.key_type) {
|
|
case COAP_PKI_KEY_PEM:
|
|
coap_log(LOG_WARNING, "RPK keys cannot be in COAP_PKI_KEY_PEM format\n");
|
|
break;
|
|
case COAP_PKI_KEY_PEM_BUF:
|
|
if (setup_data->pki_key.key.pem_buf.public_cert &&
|
|
setup_data->pki_key.key.pem_buf.public_cert[0] &&
|
|
setup_data->pki_key.key.pem_buf.private_key &&
|
|
setup_data->pki_key.key.pem_buf.private_key[0]) {
|
|
/* Need to take PEM memory information and convert to binary */
|
|
asn1_priv = pem_decode_mem_asn1("-----BEGIN EC PRIVATE KEY-----",
|
|
setup_data->pki_key.key.pem_buf.private_key);
|
|
if (!asn1_priv) {
|
|
asn1_priv = pem_decode_mem_asn1("-----BEGIN PRIVATE KEY-----",
|
|
setup_data->pki_key.key.pem_buf.private_key);
|
|
if (!asn1_priv) {
|
|
coap_log(LOG_INFO, "Private Key (RPK) invalid\n");
|
|
return 0;
|
|
}
|
|
asn1_temp = ec_abstract_pkcs8_asn1(asn1_priv->s, asn1_priv->length);
|
|
if (!asn1_temp) {
|
|
coap_log(LOG_INFO, "PKCS#8 Private Key (RPK) invalid\n");
|
|
coap_delete_binary(asn1_priv);
|
|
return 0;
|
|
}
|
|
coap_delete_binary(asn1_priv);
|
|
asn1_priv = asn1_temp;
|
|
is_pkcs8 = 1;
|
|
}
|
|
asn1_pub = pem_decode_mem_asn1(
|
|
"-----BEGIN PUBLIC KEY-----",
|
|
setup_data->pki_key.key.pem_buf.public_cert);
|
|
if (!asn1_pub) {
|
|
asn1_pub = pem_decode_mem_asn1("-----BEGIN EC PRIVATE KEY-----",
|
|
setup_data->pki_key.key.pem_buf.private_key);
|
|
if (!asn1_pub) {
|
|
asn1_pub = pem_decode_mem_asn1("-----BEGIN PRIVATE KEY-----",
|
|
setup_data->pki_key.key.pem_buf.private_key);
|
|
if (!asn1_pub) {
|
|
coap_log(LOG_INFO, "Public Key (RPK) invalid\n");
|
|
coap_delete_binary(asn1_priv);
|
|
return 0;
|
|
}
|
|
asn1_temp = ec_abstract_pkcs8_asn1(asn1_pub->s, asn1_pub->length);
|
|
if (!asn1_temp) {
|
|
coap_log(LOG_INFO, "PKCS#8 Private Key (RPK) invalid\n");
|
|
coap_delete_binary(asn1_priv);
|
|
coap_delete_binary(asn1_pub);
|
|
return 0;
|
|
}
|
|
coap_delete_binary(asn1_pub);
|
|
asn1_pub = asn1_temp;
|
|
is_pkcs8 = 1;
|
|
}
|
|
}
|
|
if (!asn1_derive_keys(t_context, asn1_priv->s, asn1_priv->length,
|
|
asn1_pub->s, asn1_pub->length, is_pkcs8)) {
|
|
coap_log(LOG_INFO, "Unable to derive Public/Private Keys\n");
|
|
coap_delete_binary(asn1_priv);
|
|
coap_delete_binary(asn1_pub);
|
|
return 0;
|
|
}
|
|
coap_delete_binary(asn1_priv);
|
|
coap_delete_binary(asn1_pub);
|
|
return 1;
|
|
}
|
|
break;
|
|
case COAP_PKI_KEY_ASN1:
|
|
if (setup_data->pki_key.key.asn1.private_key &&
|
|
setup_data->pki_key.key.asn1.private_key_len &&
|
|
setup_data->pki_key.key.asn1.private_key_type == COAP_ASN1_PKEY_EC) {
|
|
const uint8_t* private_key = setup_data->pki_key.key.asn1.private_key;
|
|
size_t private_key_len = setup_data->pki_key.key.asn1.private_key_len;
|
|
|
|
/* Check to see whether this is in pkcs8 format or not */
|
|
asn1_temp = ec_abstract_pkcs8_asn1(
|
|
setup_data->pki_key.key.asn1.private_key,
|
|
setup_data->pki_key.key.asn1.private_key_len);
|
|
if (asn1_temp) {
|
|
private_key = asn1_temp->s;
|
|
private_key_len = asn1_temp->length;
|
|
is_pkcs8 = 1;
|
|
}
|
|
/* Need to take ASN1 memory information and convert to binary */
|
|
if (setup_data->pki_key.key.asn1.public_cert &&
|
|
setup_data->pki_key.key.asn1.public_cert_len) {
|
|
if (!asn1_derive_keys(t_context,
|
|
private_key,
|
|
private_key_len,
|
|
setup_data->pki_key.key.asn1.public_cert,
|
|
setup_data->pki_key.key.asn1.public_cert_len,
|
|
is_pkcs8)) {
|
|
coap_log(LOG_INFO, "Unable to derive Public/Private Keys\n");
|
|
if (asn1_temp) coap_delete_binary(asn1_temp);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
if (!asn1_derive_keys(t_context,
|
|
private_key,
|
|
private_key_len,
|
|
private_key,
|
|
private_key_len,
|
|
is_pkcs8)) {
|
|
coap_log(LOG_INFO, "Unable to derive Public/Private Keys\n");
|
|
if (asn1_temp) coap_delete_binary(asn1_temp);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
break;
|
|
case COAP_PKI_KEY_PKCS11:
|
|
coap_log(LOG_WARNING, "RPK keys cannot be in COAP_PKI_KEY_PCKS11 format\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#else /* ! DTLS_ECC */
|
|
(void)ctx;
|
|
(void)setup_data;
|
|
#endif /* ! DTLS_ECC */
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
coap_dtls_context_set_pki_root_cas(coap_context_t *ctx COAP_UNUSED,
|
|
const char *ca_file COAP_UNUSED,
|
|
const char *ca_path COAP_UNUSED
|
|
) {
|
|
coap_log(LOG_WARNING, "Root CAs PKI not supported\n");
|
|
return 0;
|
|
}
|
|
|
|
#if COAP_CLIENT_SUPPORT
|
|
int
|
|
coap_dtls_context_set_cpsk(coap_context_t *coap_context COAP_UNUSED,
|
|
coap_dtls_cpsk_t *setup_data
|
|
) {
|
|
if (!setup_data)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
#endif /* COAP_CLIENT_SUPPORT */
|
|
|
|
#if COAP_SERVER_SUPPORT
|
|
int
|
|
coap_dtls_context_set_spsk(coap_context_t *coap_context COAP_UNUSED,
|
|
coap_dtls_spsk_t *setup_data
|
|
) {
|
|
if (!setup_data)
|
|
return 0;
|
|
|
|
if (setup_data->validate_sni_call_back) {
|
|
coap_log(LOG_WARNING,
|
|
"CoAP Server with TinyDTLS does not support SNI selection\n");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
int
|
|
coap_dtls_context_check_keys_enabled(coap_context_t *ctx COAP_UNUSED)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
#if !COAP_DISABLE_TCP
|
|
#if COAP_CLIENT_SUPPORT
|
|
void *coap_tls_new_client_session(coap_session_t *session COAP_UNUSED, int *connected COAP_UNUSED) {
|
|
return NULL;
|
|
}
|
|
#endif /* COAP_CLIENT_SUPPORT */
|
|
|
|
#if COAP_SERVER_SUPPORT
|
|
void *coap_tls_new_server_session(coap_session_t *session COAP_UNUSED, int *connected COAP_UNUSED) {
|
|
return NULL;
|
|
}
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
void coap_tls_free_session(coap_session_t *coap_session COAP_UNUSED) {
|
|
}
|
|
|
|
ssize_t coap_tls_write(coap_session_t *session COAP_UNUSED,
|
|
const uint8_t *data COAP_UNUSED,
|
|
size_t data_len COAP_UNUSED
|
|
) {
|
|
return -1;
|
|
}
|
|
|
|
ssize_t coap_tls_read(coap_session_t *session COAP_UNUSED,
|
|
uint8_t *data COAP_UNUSED,
|
|
size_t data_len COAP_UNUSED
|
|
) {
|
|
return -1;
|
|
}
|
|
#endif /* !COAP_DISABLE_TCP */
|
|
|
|
#if COAP_SERVER_SUPPORT
|
|
coap_digest_ctx_t *
|
|
coap_digest_setup(void) {
|
|
dtls_sha256_ctx *digest_ctx = coap_malloc(sizeof(dtls_sha256_ctx));
|
|
|
|
if (digest_ctx) {
|
|
dtls_sha256_init(digest_ctx);
|
|
}
|
|
|
|
return digest_ctx;
|
|
}
|
|
|
|
void
|
|
coap_digest_free(coap_digest_ctx_t *digest_ctx) {
|
|
coap_free(digest_ctx);
|
|
}
|
|
|
|
int
|
|
coap_digest_update(coap_digest_ctx_t *digest_ctx,
|
|
const uint8_t *data,
|
|
size_t data_len) {
|
|
dtls_sha256_update(digest_ctx, data, data_len);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
coap_digest_final(coap_digest_ctx_t *digest_ctx,
|
|
coap_digest_t *digest_buffer) {
|
|
dtls_sha256_final((uint8_t*)digest_buffer, digest_ctx);
|
|
|
|
coap_digest_free(digest_ctx);
|
|
return 1;
|
|
}
|
|
#endif /* COAP_SERVER_SUPPORT */
|
|
|
|
#else /* !HAVE_LIBTINYDTLS */
|
|
|
|
#ifdef __clang__
|
|
/* Make compilers happy that do not like empty modules. As this function is
|
|
* never used, we ignore -Wunused-function at the end of compiling this file
|
|
*/
|
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
|
#endif
|
|
static inline void dummy(void) {
|
|
}
|
|
|
|
#endif /* HAVE_LIBTINYDTLS */
|