/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Copyright (c) 2018, SICS, RISE AB * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /** * @file oscore_context.c * @brief An implementation of the Object Security for Constrained RESTful * Environments (RFC 8613). * * \author Martin Gunnarsson * adapted for libcoap * Peter van der Stok * on request of Fairhair alliance * adapted for libcoap integration * Jon Shallow */ #include "coap3/coap_internal.h" #include static void oscore_enter_context(coap_context_t *c_context, oscore_ctx_t *osc_ctx); static size_t compose_info(uint8_t *buffer, size_t buf_size, uint8_t alg, coap_bin_const_t *id, coap_bin_const_t *id_context, coap_str_const_t *type, size_t out_len) { size_t ret = 0; size_t rem_size = buf_size; ret += oscore_cbor_put_array(&buffer, &rem_size, 5); ret += oscore_cbor_put_bytes(&buffer, &rem_size, id ? id->s : NULL, id ? id->length : 0); if (id_context != NULL && id_context->length > 0) { ret += oscore_cbor_put_bytes(&buffer, &rem_size, id_context->s, id_context->length); } else { ret += oscore_cbor_put_nil(&buffer, &rem_size); } ret += oscore_cbor_put_unsigned(&buffer, &rem_size, alg); ret += oscore_cbor_put_text(&buffer, &rem_size, (const char *)type->s, type->length); ret += oscore_cbor_put_unsigned(&buffer, &rem_size, out_len); return ret; } uint8_t oscore_bytes_equal(uint8_t *a_ptr, uint8_t a_len, uint8_t *b_ptr, uint8_t b_len) { if (a_len != b_len) { return 0; } if (memcmp(a_ptr, b_ptr, a_len) == 0) { return 1; } else { return 0; } } static void oscore_enter_context(coap_context_t *c_context, oscore_ctx_t *osc_ctx) { if (c_context->p_osc_ctx) { oscore_ctx_t *prev = c_context->p_osc_ctx; oscore_ctx_t *next = c_context->p_osc_ctx->next; while (next) { prev = next; next = next->next; } prev->next = osc_ctx; } else c_context->p_osc_ctx = osc_ctx; } static void oscore_free_recipient(oscore_recipient_ctx_t *recipient) { coap_delete_bin_const(recipient->recipient_id); coap_delete_bin_const(recipient->recipient_key); coap_free_type(COAP_OSCORE_REC, recipient); } void oscore_free_context(oscore_ctx_t *osc_ctx) { if (osc_ctx == NULL) return; if (osc_ctx->sender_context) { coap_delete_bin_const(osc_ctx->sender_context->sender_id); coap_delete_bin_const(osc_ctx->sender_context->sender_key); coap_free_type(COAP_OSCORE_SEN, osc_ctx->sender_context); } while (osc_ctx->recipient_chain) { oscore_recipient_ctx_t *next = osc_ctx->recipient_chain->next_recipient; oscore_free_recipient(osc_ctx->recipient_chain); osc_ctx->recipient_chain = next; } coap_delete_bin_const(osc_ctx->master_secret); coap_delete_bin_const(osc_ctx->master_salt); coap_delete_bin_const(osc_ctx->id_context); coap_delete_bin_const(osc_ctx->common_iv); coap_free_type(COAP_OSCORE_COM, osc_ctx); } void oscore_free_contexts(coap_context_t *c_context) { while (c_context->p_osc_ctx) { oscore_ctx_t *osc_ctx = c_context->p_osc_ctx; c_context->p_osc_ctx = osc_ctx->next; oscore_free_context(osc_ctx); } } int oscore_remove_context(coap_context_t *c_context, oscore_ctx_t *osc_ctx) { oscore_ctx_t *prev = NULL; oscore_ctx_t *next = c_context->p_osc_ctx; while (next) { if (next == osc_ctx) { if (prev != NULL) prev->next = next->next; else c_context->p_osc_ctx = next->next; oscore_free_context(next); return 1; } prev = next; next = next->next; } return 0; } /* * oscore_find_context * Finds OSCORE context for rcpkey_id and optional ctxkey_id * rcpkey_id can be 0 length. * Updates recipient_ctx. */ oscore_ctx_t * oscore_find_context(const coap_context_t *c_context, const coap_bin_const_t rcpkey_id, const coap_bin_const_t *ctxkey_id, uint8_t *oscore_r2, oscore_recipient_ctx_t **recipient_ctx) { oscore_ctx_t *pt = c_context->p_osc_ctx; *recipient_ctx = NULL; assert(rcpkey_id.length == 0 || rcpkey_id.s != NULL); while (pt != NULL) { int ok = 0; oscore_recipient_ctx_t *rpt = pt->recipient_chain; while (rpt) { ok = 0; if (rcpkey_id.length == rpt->recipient_id->length) { if (rcpkey_id.length != 0) ok = memcmp(rpt->recipient_id->s, rcpkey_id.s, rcpkey_id.length) != 0; if (oscore_r2) { if (pt->id_context != NULL && pt->id_context->length > 8) { ok = ok + (memcmp(pt->id_context->s, oscore_r2, 8) != 0); } else { ok += 1; } } else if (ctxkey_id) { if (pt->id_context != NULL) { if (ctxkey_id->length != pt->id_context->length) ok += 1; else ok = ok + (memcmp(pt->id_context->s, ctxkey_id->s, ctxkey_id->length) != 0); } else if (ctxkey_id->length > 0) ok += 1; } if (ok == 0) { /* optional id context and recipient id are the same */ *recipient_ctx = rpt; return pt; /* OSCORE context found */ } } rpt = rpt->next_recipient; } /* while rpt */ pt = pt->next; } /* end while */ return NULL; } #define OSCORE_LOG_SIZE 16 void oscore_log_hex_value(coap_log_t level, const char *name, coap_bin_const_t *value) { size_t i; if (value == NULL) { coap_log(level, " %-16s\n", name); return; } if (value->length == 0) { coap_log(level, " %-16s <>\n", name); return; } if (coap_get_log_level() >= level) { for (i = 0; i < value->length; i += OSCORE_LOG_SIZE) { char number[3 * OSCORE_LOG_SIZE + 4]; oscore_convert_to_hex(&value->s[i], value->length - i > OSCORE_LOG_SIZE ? OSCORE_LOG_SIZE : value->length - i, number, sizeof(number)); coap_log(level, " %-16s %s\n", i == 0 ? name : "", number); } } } void oscore_log_int_value(coap_log_t level, const char *name, int value) { coap_log(level, " %-16s %2d\n", name, value); } void oscore_log_char_value(coap_log_t level, const char *name, const char *value) { coap_log(level, " %-16s %s\n", name, value); } void oscore_convert_to_hex(const uint8_t *src, size_t src_len, char *dest, size_t dst_len) { /* * Last output character will be '\000' * (If output undersized, add trailing ... to indicate this. */ size_t space = (dst_len - 4) / 3; uint32_t qq; for (qq = 0; qq < src_len && qq < space; qq++) { char tmp = src[qq] >> 4; if (tmp > 9) tmp = tmp + 0x61 - 10; else tmp = tmp + 0x30; dest[qq * 3] = tmp; tmp = src[qq] & 0xf; if (tmp > 9) tmp = tmp + 0x61 - 10; else tmp = tmp + 0x30; dest[qq * 3 + 1] = tmp; dest[qq * 3 + 2] = 0x20; } if (qq != src_len) { dest[qq * 3] = '.'; dest[qq * 3 + 1] = '.'; dest[qq * 3 + 2] = '.'; qq++; } dest[qq * 3] = 0; } static coap_bin_const_t * oscore_build_key(oscore_ctx_t *osc_ctx, coap_bin_const_t *id, coap_str_const_t *type, size_t out_len) { uint8_t info_buffer[80]; size_t info_len; uint8_t hkdf_tmp[CONTEXT_KEY_LEN > CONTEXT_INIT_VECT_LEN ? CONTEXT_KEY_LEN : CONTEXT_INIT_VECT_LEN]; info_len = compose_info(info_buffer, sizeof(info_buffer), osc_ctx->aead_alg, id, osc_ctx->id_context, type, out_len); if (info_len == 0 || info_len > sizeof(info_buffer)) return NULL; if (!oscore_hkdf(osc_ctx->hkdf_alg, osc_ctx->master_salt, osc_ctx->master_secret, info_buffer, info_len, hkdf_tmp, out_len)) return NULL; return coap_new_bin_const(hkdf_tmp, out_len); } static void oscore_log_context(oscore_ctx_t *osc_ctx, const char *heading) { #if COAP_MAX_LOGGING_LEVEL < _COAP_LOG_OSCORE (void)osc_ctx; (void)heading; #else /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_OSCORE */ if (coap_get_log_level() >= COAP_LOG_OSCORE) { char buffer[30]; oscore_recipient_ctx_t *next = osc_ctx->recipient_chain; size_t count = 0; coap_log_oscore("%s\n", heading); oscore_log_char_value(COAP_LOG_OSCORE, "AEAD alg", cose_get_alg_name(osc_ctx->aead_alg, buffer, sizeof(buffer))); oscore_log_char_value(COAP_LOG_OSCORE, "HKDF alg", cose_get_hkdf_alg_name(osc_ctx->hkdf_alg, buffer, sizeof(buffer))); oscore_log_hex_value(COAP_LOG_OSCORE, "ID Context", osc_ctx->id_context); oscore_log_hex_value(COAP_LOG_OSCORE, "Master Secret", osc_ctx->master_secret); oscore_log_hex_value(COAP_LOG_OSCORE, "Master Salt", osc_ctx->master_salt); oscore_log_hex_value(COAP_LOG_OSCORE, "Common IV", osc_ctx->common_iv); oscore_log_hex_value(COAP_LOG_OSCORE, "Sender ID", osc_ctx->sender_context->sender_id); oscore_log_hex_value(COAP_LOG_OSCORE, "Sender Key", osc_ctx->sender_context->sender_key); while (next) { snprintf(buffer, sizeof(buffer), "Recipient ID[%zu]", count); oscore_log_hex_value(COAP_LOG_OSCORE, buffer, next->recipient_id); snprintf(buffer, sizeof(buffer), "Recipient Key[%zu]", count); oscore_log_hex_value(COAP_LOG_OSCORE, buffer, next->recipient_key); count++; next = next->next_recipient; } } #endif /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_OSCORE */ } void oscore_update_ctx(oscore_ctx_t *osc_ctx, coap_bin_const_t *id_context) { coap_bin_const_t *temp; /* Update with new ID Context */ coap_delete_bin_const(osc_ctx->id_context); osc_ctx->id_context = id_context; /* Update sender_key, recipient_key and common_iv */ temp = osc_ctx->sender_context->sender_key; osc_ctx->sender_context->sender_key = oscore_build_key(osc_ctx, osc_ctx->sender_context->sender_id, coap_make_str_const("Key"), CONTEXT_KEY_LEN); if (!osc_ctx->sender_context->sender_key) osc_ctx->sender_context->sender_key = temp; else coap_delete_bin_const(temp); temp = osc_ctx->recipient_chain->recipient_key; osc_ctx->recipient_chain->recipient_key = oscore_build_key(osc_ctx, osc_ctx->recipient_chain->recipient_id, coap_make_str_const("Key"), CONTEXT_KEY_LEN); if (!osc_ctx->recipient_chain->recipient_key) osc_ctx->recipient_chain->recipient_key = temp; else coap_delete_bin_const(temp); temp = osc_ctx->common_iv; osc_ctx->common_iv = oscore_build_key(osc_ctx, NULL, coap_make_str_const("IV"), CONTEXT_INIT_VECT_LEN); if (!osc_ctx->common_iv) osc_ctx->common_iv = temp; else coap_delete_bin_const(temp); oscore_log_context(osc_ctx, "Updated Common context"); } oscore_ctx_t * oscore_duplicate_ctx(coap_context_t *c_context, oscore_ctx_t *o_osc_ctx, coap_bin_const_t *sender_id, coap_bin_const_t *recipient_id, coap_bin_const_t *id_context) { oscore_ctx_t *osc_ctx = NULL; oscore_sender_ctx_t *sender_ctx = NULL; coap_bin_const_t *copy_rid = NULL; osc_ctx = coap_malloc_type(COAP_OSCORE_COM, sizeof(oscore_ctx_t)); if (osc_ctx == NULL) goto error; memset(osc_ctx, 0, sizeof(oscore_ctx_t)); sender_ctx = coap_malloc_type(COAP_OSCORE_SEN, sizeof(oscore_sender_ctx_t)); if (sender_ctx == NULL) goto error; memset(sender_ctx, 0, sizeof(oscore_sender_ctx_t)); osc_ctx->sender_context = sender_ctx; if (o_osc_ctx->master_secret) osc_ctx->master_secret = coap_new_bin_const(o_osc_ctx->master_secret->s, o_osc_ctx->master_secret->length); if (o_osc_ctx->master_salt) osc_ctx->master_salt = coap_new_bin_const(o_osc_ctx->master_salt->s, o_osc_ctx->master_salt->length); osc_ctx->aead_alg = o_osc_ctx->aead_alg; osc_ctx->hkdf_alg = o_osc_ctx->hkdf_alg; if (id_context) osc_ctx->id_context = coap_new_bin_const(id_context->s, id_context->length); osc_ctx->ssn_freq = o_osc_ctx->ssn_freq; osc_ctx->replay_window_size = o_osc_ctx->replay_window_size; osc_ctx->rfc8613_b_1_2 = o_osc_ctx->rfc8613_b_1_2; osc_ctx->rfc8613_b_2 = o_osc_ctx->rfc8613_b_2; osc_ctx->save_seq_num_func = o_osc_ctx->save_seq_num_func; osc_ctx->save_seq_num_func_param = o_osc_ctx->save_seq_num_func_param; if (o_osc_ctx->master_secret) { /* sender_ key */ sender_ctx->sender_key = oscore_build_key(osc_ctx, sender_id, coap_make_str_const("Key"), CONTEXT_KEY_LEN); if (!sender_ctx->sender_key) goto error; /* common IV */ osc_ctx->common_iv = oscore_build_key(osc_ctx, NULL, coap_make_str_const("IV"), CONTEXT_INIT_VECT_LEN); if (!osc_ctx->common_iv) goto error; } /* * Need to set the last Sender Seq Num based on ssn_freq * The value should only change if there is a change to ssn_freq * and (potentially) be lower than seq, then save_seq_num_func() is * immediately called on next SSN update. */ sender_ctx->next_seq = 0; sender_ctx->seq = 0; sender_ctx->sender_id = coap_new_bin_const(sender_id->s, sender_id->length); copy_rid = coap_new_bin_const(recipient_id->s, recipient_id->length); if (copy_rid == NULL) goto error; if (oscore_add_recipient(osc_ctx, copy_rid, 0) == NULL) goto error; oscore_log_context(osc_ctx, "New Common context"); oscore_enter_context(c_context, osc_ctx); return osc_ctx; error: oscore_free_context(osc_ctx); return NULL; } oscore_ctx_t * oscore_derive_ctx(coap_context_t *c_context, coap_oscore_conf_t *oscore_conf) { oscore_ctx_t *osc_ctx = NULL; oscore_sender_ctx_t *sender_ctx = NULL; size_t i; osc_ctx = coap_malloc_type(COAP_OSCORE_COM, sizeof(oscore_ctx_t)); if (osc_ctx == NULL) goto error; memset(osc_ctx, 0, sizeof(oscore_ctx_t)); sender_ctx = coap_malloc_type(COAP_OSCORE_SEN, sizeof(oscore_sender_ctx_t)); if (sender_ctx == NULL) goto error; memset(sender_ctx, 0, sizeof(oscore_sender_ctx_t)); osc_ctx->sender_context = sender_ctx; osc_ctx->master_secret = oscore_conf->master_secret; osc_ctx->master_salt = oscore_conf->master_salt; osc_ctx->aead_alg = oscore_conf->aead_alg; osc_ctx->hkdf_alg = oscore_conf->hkdf_alg; osc_ctx->id_context = oscore_conf->id_context; osc_ctx->ssn_freq = oscore_conf->ssn_freq ? oscore_conf->ssn_freq : 1; osc_ctx->replay_window_size = oscore_conf->replay_window ? oscore_conf->replay_window : COAP_OSCORE_DEFAULT_REPLAY_WINDOW; osc_ctx->rfc8613_b_1_2 = oscore_conf->rfc8613_b_1_2; osc_ctx->rfc8613_b_2 = oscore_conf->rfc8613_b_2; osc_ctx->save_seq_num_func = oscore_conf->save_seq_num_func; osc_ctx->save_seq_num_func_param = oscore_conf->save_seq_num_func_param; if (oscore_conf->master_secret) { /* sender_ key */ if (oscore_conf->break_sender_key) /* Interop testing */ sender_ctx->sender_key = oscore_build_key(osc_ctx, oscore_conf->sender_id, coap_make_str_const("BAD"), CONTEXT_KEY_LEN); else sender_ctx->sender_key = oscore_build_key(osc_ctx, oscore_conf->sender_id, coap_make_str_const("Key"), CONTEXT_KEY_LEN); if (!sender_ctx->sender_key) goto error; /* common IV */ osc_ctx->common_iv = oscore_build_key(osc_ctx, NULL, coap_make_str_const("IV"), CONTEXT_INIT_VECT_LEN); if (!osc_ctx->common_iv) goto error; } /* * Need to set the last Sender Seq Num based on ssn_freq * The value should only change if there is a change to ssn_freq * and (potentially) be lower than seq, then save_seq_num_func() is * immediately called on next SSN update. */ sender_ctx->next_seq = oscore_conf->start_seq_num - (oscore_conf->start_seq_num % (oscore_conf->ssn_freq > 0 ? oscore_conf->ssn_freq : 1)); sender_ctx->sender_id = oscore_conf->sender_id; sender_ctx->seq = oscore_conf->start_seq_num; for (i = 0; i < oscore_conf->recipient_id_count; i++) { if (oscore_add_recipient(osc_ctx, oscore_conf->recipient_id[i], oscore_conf->break_recipient_key) == NULL) { coap_log_warn("OSCORE: Failed to add Client ID\n"); goto error; } } oscore_log_context(osc_ctx, "Common context"); oscore_enter_context(c_context, osc_ctx); return osc_ctx; error: coap_free_type(COAP_OSCORE_COM, osc_ctx); coap_free_type(COAP_OSCORE_SEN, sender_ctx); return NULL; } oscore_recipient_ctx_t * oscore_add_recipient(oscore_ctx_t *osc_ctx, coap_bin_const_t *rid, uint32_t break_key) { oscore_recipient_ctx_t *rcp_ctx = osc_ctx->recipient_chain; oscore_recipient_ctx_t *recipient_ctx = NULL; if (rid->length > 7) { coap_log_warn("oscore_add_recipient: Maximum size of recipient_id is 7 bytes\n"); return NULL; } /* Check this is not a duplicate recipient id */ while (rcp_ctx) { if (rcp_ctx->recipient_id->length == rid->length && memcmp(rcp_ctx->recipient_id->s, rid->s, rid->length) == 0) { coap_delete_bin_const(rid); return NULL; } rcp_ctx = rcp_ctx->next_recipient; } recipient_ctx = (oscore_recipient_ctx_t *)coap_malloc_type( COAP_OSCORE_REC, sizeof(oscore_recipient_ctx_t)); if (recipient_ctx == NULL) return NULL; memset(recipient_ctx, 0, sizeof(oscore_recipient_ctx_t)); if (osc_ctx->master_secret) { if (break_key) /* Interop testing */ recipient_ctx->recipient_key = oscore_build_key(osc_ctx, rid, coap_make_str_const("BAD"), CONTEXT_KEY_LEN); else recipient_ctx->recipient_key = oscore_build_key(osc_ctx, rid, coap_make_str_const("Key"), CONTEXT_KEY_LEN); if (!recipient_ctx->recipient_key) { coap_free_type(COAP_OSCORE_REC, recipient_ctx); return NULL; } } recipient_ctx->recipient_id = rid; recipient_ctx->initial_state = 1; recipient_ctx->osc_ctx = osc_ctx; rcp_ctx = osc_ctx->recipient_chain; recipient_ctx->next_recipient = rcp_ctx; osc_ctx->recipient_chain = recipient_ctx; return recipient_ctx; } int oscore_delete_recipient(oscore_ctx_t *osc_ctx, coap_bin_const_t *rid) { oscore_recipient_ctx_t *prev = NULL; oscore_recipient_ctx_t *next = osc_ctx->recipient_chain; while (next) { if (next->recipient_id->length == rid->length && memcmp(next->recipient_id->s, rid->s, rid->length) == 0) { if (prev != NULL) prev->next_recipient = next->next_recipient; else osc_ctx->recipient_chain = next->next_recipient; oscore_free_recipient(next); return 1; } prev = next; next = next->next_recipient; } return 0; } void oscore_free_association(oscore_association_t *association) { if (association) { coap_delete_pdu(association->sent_pdu); coap_delete_bin_const(association->token); coap_delete_bin_const(association->aad); coap_delete_bin_const(association->nonce); coap_delete_bin_const(association->partial_iv); coap_free_type(COAP_STRING, association); } } int oscore_new_association(coap_session_t *session, coap_pdu_t *sent_pdu, coap_bin_const_t *token, oscore_recipient_ctx_t *recipient_ctx, coap_bin_const_t *aad, coap_bin_const_t *nonce, coap_bin_const_t *partial_iv, int is_observe) { oscore_association_t *association; association = coap_malloc_type(COAP_STRING, sizeof(oscore_association_t)); if (association == NULL) return 0; memset(association, 0, sizeof(oscore_association_t)); association->recipient_ctx = recipient_ctx; association->is_observe = is_observe; if (sent_pdu) { size_t size; const uint8_t *data; association->sent_pdu = coap_pdu_duplicate(sent_pdu, session, token->length, token->s, NULL); if (association->sent_pdu == NULL) goto error; if (coap_get_data(sent_pdu, &size, &data)) { coap_add_data(association->sent_pdu, size, data); } } association->token = coap_new_bin_const(token->s, token->length); if (association->token == NULL) goto error; if (aad) { association->aad = coap_new_bin_const(aad->s, aad->length); if (association->aad == NULL) goto error; } if (nonce) { association->nonce = coap_new_bin_const(nonce->s, nonce->length); if (association->nonce == NULL) goto error; } if (partial_iv) { association->partial_iv = coap_new_bin_const(partial_iv->s, partial_iv->length); if (association->partial_iv == NULL) goto error; } OSCORE_ASSOCIATIONS_ADD(session->associations, association); return 1; error: oscore_free_association(association); return 0; } oscore_association_t * oscore_find_association(coap_session_t *session, coap_bin_const_t *token) { oscore_association_t *association; OSCORE_ASSOCIATIONS_FIND(session->associations, token, association); return association; } int oscore_delete_association(coap_session_t *session, oscore_association_t *association) { if (association) { OSCORE_ASSOCIATIONS_DELETE(session->associations, association); oscore_free_association(association); return 1; } return 0; } void oscore_delete_server_associations(coap_session_t *session) { if (session) { oscore_association_t *association; oscore_association_t *tmp; OSCORE_ASSOCIATIONS_ITER_SAFE(session->associations, association, tmp) { OSCORE_ASSOCIATIONS_DELETE(session->associations, association); oscore_free_association(association); } session->associations = NULL; } }