COAP_OSCORE(3)

NAME
coap_oscore, coap_new_oscore_conf, coap_delete_oscore_conf, coap_new_oscore_recipient, coap_delete_oscore_recipient, coap_new_client_session_oscore, coap_new_client_session_oscore_pki, coap_new_client_session_oscore_psk, coap_context_oscore_server - Work with CoAP OSCORE

SYNOPSIS

#include <coap3/coap.h>

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, -lcoap-3-wolfssl 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:

    /**
     * Definition of the function used to save the current Sender Sequence Number
     *
     * @param sender_seq_num The Sender Sequence Number to save in non-volatile
     *                      memory.
     * @param param The save_seq_num_func_param provided to
     *              coap_new_oscore_context().
     *
     * @return @c 1 if success, else @c 0 if a failure of some sort.
     */
    typedef int (*coap_oscore_save_seq_num_t)(uint64_t sender_seq_num, void *param);

FUNCTIONS

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).

RETURN VALUES

coap_new_client_session_oscore(), coap_new_client_session_oscore_psk() and coap_new_client_session_oscore_pki() return a newly created client session or NULL if there is a malloc or parameter failure.

coap_new_oscore_conf() returns a coap_oscore_conf_t or NULL on failure.

coap_context_oscore_server(), coap_delete_oscore_conf(), coap_new_oscore_recipient() and coap_delete_oscore_recipient() return 0 on failure, 1 on success.

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;
    }

SEE ALSO

coap_endpoint_client(3), coap_endpoint_server(3), coap-oscore-conf(5) and coap_supported(3)

FURTHER INFORMATION

See

"RFC7252: The Constrained Application Protocol (CoAP)"

"RFC8613: Object Security for Constrained RESTful Environments (OSCORE)"

for further information.

BUGS

Please raise an issue on GitHub at https://github.com/obgm/libcoap/issues to report any bugs.

Please raise a Pull Request at https://github.com/obgm/libcoap/pulls for any fixes.

AUTHORS

The libcoap project <libcoap-developers@lists.sourceforge.net>