SYNOPSIS
#include <coap3/coap.h>
int coap_oscore_is_supported(void);
coap_oscore_conf_t *coap_new_oscore_conf(coap_str_const_t conf_mem,
coap_oscore_save_seq_num_t save_seq_num_func,
void *save_seq_num_func_param, uint64_t start_seq_num);
int coap_delete_oscore_conf(coap_oscore_conf_t *oscore_conf);
int coap_new_oscore_recipient(coap_context_t *context,
coap_bin_const_t *recipient_id);
int coap_delete_oscore_recipient(coap_context_t *context,
coap_bin_const_t *recipient_id);
coap_session_t *coap_new_client_session_oscore(coap_context_t *context,
const coap_address_t *local_if, const coap_address_t *server,
coap_proto_t proto, coap_oscore_conf_t *oscore_conf);
coap_session_t *coap_new_client_session_oscore_psk(coap_context_t *context,
const coap_address_t *local_if, const coap_address_t *server,
coap_proto_t proto, coap_dtls_cpsk_t *psk_data,
coap_oscore_conf_t *oscore_conf);
coap_session_t *coap_new_client_session_oscore_pki(coap_context_t *context,
const coap_address_t *local_if, const coap_address_t *server,
coap_proto_t proto, coap_dtls_pki_t *pki_data,
coap_oscore_conf_t *oscore_conf);
int coap_context_oscore_server(coap_context_t *context,
coap_oscore_conf_t *oscore_conf);
For specific (D)TLS library support, link with
-lcoap-3-notls, -lcoap-3-gnutls,
-lcoap-3-openssl, -lcoap-3-mbedtls
or -lcoap-3-tinydtls. Otherwise, link with
-lcoap-3 to get the default (D)TLS library support.
DESCRIPTION
This describes libcoap’s support for using OSCORE as defined in
RFC8613.
OSCORE provides end-to-end protection between endpoints communicating using
CoAP. (D)TLS can only protect HOP by HOP traffic which allows proxies to
manipulate information.
CALLBACK HANDLER
Callback Type: coap_oscore_save_seq_num_t
The coap_oscore_save_seq_num_t method handler function prototype is defined as:
typedef int (*coap_oscore_save_seq_num_t)(uint64_t sender_seq_num, void *param);
and returns 0 on failure, 1 on succes.
FUNCTIONS
Function: coap_oscore_is_supported()
The coap_oscore_is_supported() function returns 1 if OSCORE is supported,
otherwise 0.
Function: coap_new_oscore_conf()
The coap_new_oscore_conf() function is used to build a new OSCORE
configuration. It parses the provided OSCORE configuration
in conf_mem. The format of the keywords, encoding types and values is
documented in coap-oscore-conf(5). It also sets an optional function
save_seq_num_func (which gets save_seq_num_func_param passed in) that is
called to store the next Sender Sequence Number (SSN) in non-volatile
storage. The latest next SSN from non-volatile storage (or 0) is then put in
start_seq_num. SSN are used so that anti-replay mechanisms do not kick in
following application restarts. The rate of calling save_seq_num_func can
be controlled by the ssn_freq parameter as defined in coap-oscore-conf(5).
This OSCORE configuration is then used in the client and server OSCORE version
of the setup functions.
If the server is proxy enabled and the new incoming session is OSCORE encoded
with the Outer CoAP ProxyScheme and Host options set, then the libcoap logic
will determine whether the server is the final endpoint of the session, or
whether the proxy will be forwarding the session off to another server, based
on how coap_resource_proxy_uri_init(3) configured the proxy logic. If the
session is going to forwarded, then the OSCORE protection will not be removed.
If coap_context_oscore_server() is not called and the proxy logic (if set)
indicates the session will get forwarded, then the OSCORE protection is
untouched, otherwise the session will get dropped with an unknown critical
option error response.
Function: coap_new_oscore_recipient()
The coap_new_oscore_recipient() is used to add a new recipient_id to the
OSCORE information associated with context. The new recipient_id should
be unique and this function should only be used for server based applications
as the client will only ever use the first defined recipient_id. It is
assumed that coap_context_oscore_server() has already been called to update
context with the appropriate OSCORE information.
Function: coap_delete_oscore_recipient()
The coap_delete_oscore_recipient() is used to remove the recipient_id from
the OSCORE information associated with context. OSCORE Traffic continuing
to use recipient_id will then fail.
Function: coap_delete_oscore_conf()
The coap_delete_oscore_conf() function is used to free off the
coap_oscore_conf_t structure oscore_conf as returned by
coap_new_oscore_conf(). Normally this function never needs to be called
as oscore_conf is freed off by the call the client or server setup functions.
Function: coap_new_client_session_oscore()
The coap_new_client_session_oscore() is basically the same as
coap_new_client_session(3), but has an additional parameter oscore_conf
that is created by coap_new_oscore_conf(). This function creates a
client endpoint for a specific context and initiates a new client session
to the specified server using the CoAP protocol proto and OSCORE
protected by the oscore_conf definition (which is freed off by this call).
If the port is set to 0 in server, then the default CoAP port is used.
Normally local_if would be set to NULL, but by specifying local_if the
source of the network session can be bound to a specific IP address or port.
The session will initially have a reference count of 1.
Function: coap_new_client_session_oscore_psk()
The coap_new_client_session_oscore_psk() is basically the same as
coap_new_client_session_psk2(3), but has an additional parameter
oscore_conf that is created by coap_new_oscore_conf(). This
function, for a specific context, is used to configure the TLS context
using the setup_data variables as defined in the coap_dtls_cpsk_t structure
in the newly created endpoint session - see coap_encryption(3),
as well as OSCORE protected by the oscore_conf definition (which is freed
off by this call). The connection is to the specified server using the CoAP
protocol proto. If the port is set to 0 in server, then the default CoAP
port is used. Normally local_if would be set to NULL, but by specifying
local_if the source of the network session can be bound to a specific IP
address or port. The session will initially have a reference count of 1.
Function: coap_new_client_session_oscore_pki()
The coap_new_client_session_oscore_pki() is basically the same as
coap_new_client_session_pki(3), but has an additional parameter
oscore_conf that is created by coap_new_oscore_conf(). This
function, for a specific context, is used to configure the TLS context using
the setup_data variables as defined in the coap_dtls_pki_t structure in the
newly created endpoint session - see coap_encryption(3), as well as OSCORE
protected by the oscore_conf definition (which is freed off by this call).
The connection is to the specified server using the CoAP protocol proto.
If the port is set to 0 in server, then the default CoAP port is used.
Normally local_if would be set to NULL, but by specifying local_if the
source of the network session can be bound to a specific IP address or port.
The session will initially have a reference count of 1.
Function: coap_context_oscore_server()
The coap_context_oscore_server() function is used to enable the server to
support OSCORE incoming sessions. It updates context with the OSCORE
configure oscore_conf (which is freed off by this call).
EXAMPLES
Client Setup
#include <coap3/coap.h>
#include <netinet/in.h>
#include <stdio.h>
static uint8_t oscore_config[] =
"master_secret,hex,\"0102030405060708090a0b0c0d0e0f10\"\n"
"master_salt,hex,\"9e7ca92223786340\"\n"
"server_id,ascii,\"client\"\n"
"recipient_id,ascii,\"server\"\n"
"replay_window,integer,30\n"
"aead_alg,integer,10\n"
"hkdf_alg,integer,-10\n"
;
static FILE *oscore_seq_num_fp = NULL;
/* Not a particularly safe place to keep next Sender Sequence Number ... */
static const char* oscore_seq_save_file = "/tmp/client.seq";
static int
oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
if (oscore_seq_num_fp) {
rewind(oscore_seq_num_fp);
fprintf(oscore_seq_num_fp, "%lu\n", sender_seq_num);
fflush(oscore_seq_num_fp);
}
return 1;
}
static coap_session_t *
setup_client_session (struct in_addr ip_address) {
coap_session_t *session;
coap_address_t server;
/* See coap_context(3) */
coap_context_t *context = coap_new_context(NULL);
if (!context)
return NULL;
/* See coap_block(3) */
coap_context_set_block_mode(context,
COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
coap_address_init(&server);
server.addr.sa.sa_family = AF_INET;
server.addr.sin.sin_addr = ip_address;
server.addr.sin.sin_port = htons (5683);
if (coap_oscore_is_supported()) {
coap_str_const_t config = { sizeof (oscore_config), oscore_config };
uint64_t start_seq_num = 0;
coap_oscore_conf_t *oscore_conf;
if (oscore_seq_save_file) {
oscore_seq_num_fp = fopen(oscore_seq_save_file, "r+");
if (oscore_seq_num_fp == NULL) {
/* Try creating it */
oscore_seq_num_fp = fopen(oscore_seq_save_file, "w+");
if (oscore_seq_num_fp == NULL) {
coap_log_err("OSCORE save restart info file error: %s\n",
oscore_seq_save_file);
return NULL;
}
}
fscanf(oscore_seq_num_fp, "%ju", &start_seq_num);
}
oscore_conf = coap_new_oscore_conf(config, oscore_save_seq_num,
NULL, start_seq_num);
if (!oscore_conf) {
coap_free_context(context);
return NULL;
}
session = coap_new_client_session_oscore(context, NULL, &server,
COAP_PROTO_UDP, oscore_conf);
} else {
session = coap_new_client_session(context, NULL, &server, COAP_PROTO_UDP);
}
if (!session) {
coap_free_context(context);
return NULL;
}
/* The context is in session->context */
return session;
}
Server Setup
#include <coap3/coap.h>
#include <stdio.h>
static uint8_t oscore_config[] =
"master_secret,hex,\"0102030405060708090a0b0c0d0e0f10\"\n"
"master_salt,hex,\"9e7ca92223786340\"\n"
"sender_id,ascii,\"server\"\n"
"recipient_id,ascii,\"client\"\n"
"replay_window,integer,30\n"
"aead_alg,integer,10\n"
"hkdf_alg,integer,-10\n"
;
static FILE *oscore_seq_num_fp = NULL;
/* Not a particularly safe place to keep next Sender Sequence Number ... */
static const char* oscore_seq_save_file = "/tmp/server.seq";
static int
oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
if (oscore_seq_num_fp) {
rewind(oscore_seq_num_fp);
fprintf(oscore_seq_num_fp, "%lu\n", sender_seq_num);
fflush(oscore_seq_num_fp);
}
return 1;
}
static int
setup_context (void) {
/* See coap_context(3) */
coap_context_t *context = coap_new_context(NULL);
if (!context)
return 0;
/* See coap_block(3) */
coap_context_set_block_mode(context,
COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
if (coap_oscore_is_supported()) {
coap_str_const_t config = { sizeof (oscore_config), oscore_config };
uint64_t start_seq_num = 0;
coap_oscore_conf_t *oscore_conf;
if (oscore_seq_save_file) {
oscore_seq_num_fp = fopen(oscore_seq_save_file, "r+");
if (oscore_seq_num_fp == NULL) {
/* Try creating it */
oscore_seq_num_fp = fopen(oscore_seq_save_file, "w+");
if (oscore_seq_num_fp == NULL) {
coap_log_err("OSCORE save restart info file error: %s\n",
oscore_seq_save_file);
return 0;
}
}
fscanf(oscore_seq_num_fp, "%ju", &start_seq_num);
}
oscore_conf = coap_new_oscore_conf(config, oscore_save_seq_num,
NULL, start_seq_num);
if (!oscore_conf) {
coap_free_context(context);
return 0;
}
coap_context_oscore_server(context, oscore_conf);
}
return 1;
}