Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 261 additions & 4 deletions examples/coap-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ static ssize_t user_length = -1;

static coap_dtls_pki_t *setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *sni);

static uint8_t *read_file_mem(const char *file, size_t *length);

static char *oscore_make_credential_file_name(const coap_bin_const_t *rcpkey_id,
const coap_bin_const_t *ctxkey_id, int seq_file);

typedef struct psk_sni_def_t {
char *sni_match;
coap_bin_const_t *new_key;
Expand Down Expand Up @@ -302,6 +307,194 @@ hnd_get_index(coap_resource_t *resource,
(const uint8_t *)INDEX, NULL, NULL);
}

/** Store an active echo challange, only one available at a time */
static struct {
uint8_t recipient_id[10];
uint8_t recipient_id_len;
uint8_t context_id[10];
uint8_t context_id_len;
uint8_t echo_value[8];
} active_echo_value;

/* compare recipient_id and ctxkey_id (optional) with the active active_echo_value */
static int
coap_oscore_active_echo_match(const coap_bin_const_t *recipient_id,
const coap_bin_const_t *ctxkey_id) {
if (recipient_id == NULL)
return 0;

/* validate recipient id */
if (active_echo_value.recipient_id_len != recipient_id->length ||
memcmp(active_echo_value.recipient_id, recipient_id->s, recipient_id->length) != 0) {
return 0;
}

if (ctxkey_id == NULL && active_echo_value.context_id_len == 0)
return 1;

if (ctxkey_id == NULL || ctxkey_id->s == NULL || ctxkey_id->length == 0)
return active_echo_value.context_id_len == 0;

return active_echo_value.context_id_len == ctxkey_id->length &&
memcmp(active_echo_value.context_id, ctxkey_id->s, ctxkey_id->length) == 0;
}

static int
update_seq_num_handler(
const coap_session_t *session,
const coap_bin_const_t rcpkey_id,
const coap_bin_const_t ctxkey_id,
uint64_t sender_seq_num,
uint64_t seq_num_window
) {
(void)session;
char *seq_file = oscore_make_credential_file_name(&rcpkey_id, &ctxkey_id, 1);
if (seq_file == NULL)
return 0;

FILE *fp = fopen(seq_file, "w");
if (fp == NULL) {
coap_free_type(COAP_STRING, seq_file);
return 0;
}

int ret = fprintf(fp, "%" PRIu64 " %" PRIu64 "\n", sender_seq_num, seq_num_window);
fclose(fp);
coap_free_type(COAP_STRING, seq_file);
return ret > 0;
}

static int
update_echo_handler(
const coap_session_t *session,
const coap_bin_const_t rcpkey_id,
const coap_bin_const_t ctxkey_id,
const uint8_t echo_value[8],
int store_value
) {
(void)session;

if (store_value == 0) {
if (coap_oscore_active_echo_match(&rcpkey_id, &ctxkey_id)) {
/* read echo value from storage */
memcpy(active_echo_value.echo_value, echo_value, sizeof(active_echo_value.echo_value));
return 1;
}
return 0;
}

if (echo_value == NULL) {
/* clear the active echo value */
active_echo_value.recipient_id_len = 0;
active_echo_value.context_id_len = 0;
memset(active_echo_value.echo_value, 0, sizeof(active_echo_value.echo_value));
return 1;
}

active_echo_value.context_id_len = ctxkey_id.length ? (uint8_t)min(ctxkey_id.length,
sizeof(active_echo_value.context_id)) : 0;
if (ctxkey_id.length) {
memcpy(active_echo_value.context_id, ctxkey_id.s, active_echo_value.context_id_len);
}

active_echo_value.recipient_id_len = (uint8_t)rcpkey_id.length;
memcpy(active_echo_value.recipient_id, rcpkey_id.s, active_echo_value.recipient_id_len);
memcpy(active_echo_value.echo_value, echo_value, sizeof(active_echo_value.echo_value));
return 1;
}

static int
load_oscore_storage_seq_file(
const coap_bin_const_t *rcpkey_id,
const coap_bin_const_t *ctxkey_id,
uint64_t *sender_seq_num,
uint64_t *seq_num_window
) {
char *seq_file = oscore_make_credential_file_name(rcpkey_id, ctxkey_id, 1);
if (seq_file == NULL)
return 0;

FILE *fp = fopen(seq_file, "r");
if (fp == NULL) {
coap_free_type(COAP_STRING, seq_file);
return 0;
}

int ret = fscanf(fp, "%" SCNu64 " %" SCNu64 "\n", sender_seq_num,
seq_num_window);
fclose(fp);
coap_free_type(COAP_STRING, seq_file);
return ret == 2;
}

/**
* Load the config from file system if file exists.
* Demonstrate external credential storage, this implementation is
* only for demonstration and not meant for production use.
* The approach is not efficient.
*/
static coap_oscore_conf_t *
coap_oscore_ctx_find(
const coap_session_t *session,
const coap_bin_const_t rcpkey_id,
const coap_bin_const_t ctxkey_id
) {
(void)session;

size_t length;
uint8_t *buf = NULL;
char *cred_file = oscore_make_credential_file_name(&rcpkey_id, &ctxkey_id, 0);
char *seq_file = oscore_make_credential_file_name(&rcpkey_id, &ctxkey_id, 1);
coap_oscore_conf_t *result = NULL;

/* load oscore context from file if available, and return recipient */
buf = read_file_mem(cred_file, &length);
if (buf == NULL) {
coap_log_info("No credentials file '%s' found\n", cred_file);
goto exit;
}

result = coap_new_oscore_conf((coap_str_const_t) {
length, buf
},
NULL, NULL, 0);
if (result == NULL) {
coap_log_info("failed to load credentials file %s\n", cred_file);
goto exit;
}

/* Ensure existing rcp conf is removed,
should the rcp with same keyid already exist. */
coap_delete_oscore_conf_rcp(result, &rcpkey_id);

coap_oscore_rcp_conf_t *rcp = coap_oscore_conf_add_rcp(result, &rcpkey_id);
if (rcp == NULL) {
coap_log_info("failed to add rcp for %s\n", cred_file);
goto exit;
}

coap_log_debug("open credentials file %s\n", cred_file);

uint64_t sender_seq_num = 0;
uint64_t seq_num_window = 0;
if (load_oscore_storage_seq_file(&rcpkey_id, &ctxkey_id, &sender_seq_num, &seq_num_window)) {
coap_oscore_rcp_conf_set_seq_num(rcp, sender_seq_num, seq_num_window);
}

/* load echo value if available */
if (coap_oscore_active_echo_match(&rcpkey_id, &ctxkey_id)) {
coap_oscore_rcp_conf_set_echo(rcp, active_echo_value.echo_value);
}

coap_oscore_conf_set_context_id(result, &ctxkey_id);

exit:
coap_free_type(COAP_STRING, cred_file);
coap_free_type(COAP_STRING, seq_file);
coap_free(buf);
return result;
}

static void
hnd_get_fetch_time(coap_resource_t *resource,
coap_session_t *session,
Expand Down Expand Up @@ -1672,7 +1865,7 @@ usage(const char *program, const char *version) {
"\t\t[-q tls_engine_conf_file] [-r] [-v num] [-w [port][,secure_port]]\n"
"\t\t[-x] [-y rec_secs] [-z scheme://addr[:port][/resource[?query]]]\n"
"\t\t[-A address] [-B resource[:check]] [-E oscore_conf_file[,seq_file]]\n"
"\t\t[-G group_if] [-L value] [-N]\n"
"\t\t[-O oscore_cred_dir] [-G group_if] [-L value] [-N]\n"
"\t\t[-P scheme://address[:port],[name1[,name2..]]]\n"
"\t\t[-T max_token_size] [-U type] [-V num] [-X size] [-3]\n"
"\t\t[[-h hint] [-i match_identity_file] [-k key]\n"
Expand All @@ -1681,6 +1874,8 @@ usage(const char *program, const char *version) {
"\t\t[-J pkcs11_pin] [-M rpk_file] [-R trust_casfile]\n"
"\t\t[-S match_pki_sni_file] [-Y]]\n"
"General Options\n"
, program);
fprintf(stderr,
"\t-a priority\tSend logging output to syslog at priority (0-7) level\n"
"\t-b max_block_size\n"
"\t \t\tMaximum block size server supports (16, 32, 64,\n"
Expand Down Expand Up @@ -1743,14 +1938,27 @@ usage(const char *program, const char *version) {
"\t \t\toptional check key as a query option (check=vvv). Query\n"
"\t \t\tinformation in the PUT request can define the IP (ip=xxx)\n"
"\t \t\tand port (port=yyy) the downstream client should be\n"
"\t \t\tspecifying to connect to in the proxy request\n"
, program);
"\t \t\tspecifying to connect to in the proxy request\n");
fprintf(stderr,
"\t-E oscore_conf_file[,seq_file]\n"
"\t \t\toscore_conf_file contains OSCORE configuration. See\n"
"\t \t\tcoap-oscore-conf(5) for definitions.\n"
"\t \t\tOptional seq_file is used to save the current transmit\n"
"\t \t\tsequence number, so on restart sequence numbers continue\n"
"\t-O oscore_cred_dir\n"
"\t \t\tDirectory containing OSCORE credential files dynamically\n"
"\t \t\tloaded in format <contextid>_<kid>.txt where contextid\n"
"\t \t\tand kid are hex-encoded byte strings.\n"
"\t \t\tEach file follows the coap-oscore-conf(5) format, whereby\n"
"\t \t\tcontextid and kid are ignored, the filename entries values\n"
"\t \t\tare used instead.\n"
"\t \t\tDemonstrates the use of an external OSCORE credential store.\n"
"\t \t\tA <contextid>_<kid>_seq.txt file contains the recipient\n"
"\t \t\tsequence counter and window. To enforce a new echo challenge\n"
"\t \t\tthe <contextid>_<kid>_seq.txt file can be deleted, to remove\n"
"\t \t\tthe credentials, the <contextid>_<kid>.txt file can be deleted.\n"
"\t \t\tCan be combined with -E for a default context.\n");
fprintf(stderr,
"\t-G group_if\tUse this interface for listening for the multicast\n"
"\t \t\tgroup. This can be different from the implied interface\n"
"\t \t\tif the -A option is used\n"
Expand Down Expand Up @@ -2175,6 +2383,7 @@ cmdline_read_user(char *arg, unsigned char **buf, size_t maxlen) {
static FILE *oscore_seq_num_fp = NULL;
static const char *oscore_conf_file = NULL;
static const char *oscore_seq_save_file = NULL;
static const char *oscore_cred_dir = NULL;

static int
oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
Expand All @@ -2186,6 +2395,38 @@ oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
return 1;
}


/*
* Build "${oscore_cred_dir}/<ctxkey_id_hex>_<rcpkey_id_hex>[_seq].txt".
* Caller must free with coap_free().
*/
static char *
oscore_make_credential_file_name(const coap_bin_const_t *rcpkey_id,
const coap_bin_const_t *ctxkey_id, int seq_file) {
size_t rcp_len = rcpkey_id ? rcpkey_id->length : 0;
size_t ctx_len = ctxkey_id ? ctxkey_id->length : 0;
size_t dir_len = oscore_cred_dir ? strlen(oscore_cred_dir) + 1 : 0; /* "dir/" */
size_t buf_len = dir_len + (2 * ctx_len) + 1 + (2 * rcp_len) +
9; /* hex + "_" + hex + "[_seq].txt\0" */
char *result = coap_malloc_type(COAP_STRING, buf_len);
if (!result)
return NULL;

char *p = result;
char *end = result + buf_len;
if (oscore_cred_dir)
p += snprintf(p, end - p, "%s", oscore_cred_dir);
for (size_t i = 0; i < ctx_len; i++)
p += snprintf(p, end - p, "%02x", ctxkey_id->s[i]);
*p++ = '_';
for (size_t i = 0; i < rcp_len; i++)
p += snprintf(p, end - p, "%02x", rcpkey_id->s[i]);
if (seq_file)
p += snprintf(p, end - p, "_seq");
snprintf(p, end - p, ".txt");
return result;
}

static coap_oscore_conf_t *
get_oscore_conf(coap_context_t *context) {
uint8_t *buf;
Expand Down Expand Up @@ -2635,7 +2876,7 @@ main(int argc, char **argv) {
clock_offset = time(NULL);

while ((opt = getopt(argc, argv,
"a:b:c:d:ef:g:h:i:j:k:l:mnop:q:rs:tu:v:w:xy:z:A:B:C:E:G:J:L:M:NP:R:S:T:U:V:X:Y23")) != -1) {
"a:b:c:d:ef:g:h:i:j:k:l:mnop:q:rs:tu:v:w:xy:z:A:B:C:E:G:J:L:M:NP:O:R:S:T:U:V:X:Y23")) != -1) {
switch (opt) {
#ifndef _WIN32
case 'a':
Expand Down Expand Up @@ -2746,6 +2987,13 @@ main(int argc, char **argv) {
case 'N':
resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_NON;
break;
case 'O':
if (!coap_oscore_is_supported()) {
fprintf(stderr, "OSCORE support not enabled\n");
goto failed;
}
oscore_cred_dir = optarg;
break;
case 'o':
shutdown_no_observe = 1;
break;
Expand Down Expand Up @@ -2908,6 +3156,15 @@ main(int argc, char **argv) {
if (get_oscore_conf(ctx) == NULL)
goto failed;
}
if (oscore_cred_dir) {
/* register example local OSCORE credential storage */
coap_oscore_register_external_handlers(
ctx,
coap_oscore_ctx_find,
update_seq_num_handler,
update_echo_handler
);
}
#if COAP_PROXY_SUPPORT
if (reverse_proxy.entry_count) {
proxy_dtls_setup(ctx, &reverse_proxy);
Expand Down
2 changes: 2 additions & 0 deletions include/coap3/coap_forward_decls.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ typedef struct coap_queue_t coap_queue_t;
* OSCORE information.
*/
typedef struct coap_oscore_conf_t coap_oscore_conf_t;
typedef struct coap_oscore_rcp_conf_t coap_oscore_rcp_conf_t;
typedef struct coap_oscore_snd_conf_t coap_oscore_snd_conf_t;

/* ************* coap_pdu_internal.h ***************** */

Expand Down
6 changes: 6 additions & 0 deletions include/coap3/coap_net_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "coap_subscribe.h"
#include "coap_resource.h"
#include "coap_oscore.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -102,6 +103,11 @@ struct coap_context_t {
#endif /* RIOT_VERSION */
#if COAP_OSCORE_SUPPORT
struct oscore_ctx_t *p_osc_ctx; /**< primary oscore context */
coap_oscore_find_handler_t oscore_find_cb; /**< Optional override for oscore_find_context() */
coap_oscore_update_echo_handler_t
oscore_update_echo_cb; /**< Optional function to call to update echo values */
coap_oscore_update_seq_num_handler_t
oscore_update_seq_num_cb; /**< Optional function to call to update sequence number and window values */
#endif /* COAP_OSCORE_SUPPORT */

#if COAP_CLIENT_SUPPORT
Expand Down
Loading
Loading