diff --git a/api/oc_client_role.c b/api/oc_client_role.c index e6cc10d61..3159d1782 100644 --- a/api/oc_client_role.c +++ b/api/oc_client_role.c @@ -22,7 +22,9 @@ #include "oc_role.h" #include "port/oc_log_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_roles_internal.h" +#include "util/oc_secure_string_internal.h" oc_role_t * oc_get_all_roles(void) @@ -65,10 +67,23 @@ oc_assert_role(const char *role, const char *authority, if (oc_tls_uses_psk_cred(oc_tls_get_peer(endpoint))) { return false; } - const oc_sec_cred_t *cr = - oc_sec_find_role_cred(/*start*/ NULL, role, authority, - /*tag*/ NULL); // ignore tag, we want to serialize - // only the [role,authority] pairs + + oc_string_view_t role_view = + oc_string_view(role, oc_strnlen_s(role, OC_MAX_STRING_LENGTH)); + if (role_view.data == NULL || role_view.length == OC_MAX_STRING_LENGTH) { + OC_ERR("invalid role"); + return false; + } + oc_string_view_t authority_view = + oc_string_view(authority, oc_strnlen_s(authority, OC_MAX_STRING_LENGTH)); + if (authority_view.length == OC_MAX_STRING_LENGTH) { + OC_ERR("invalid authority"); + return false; + } + const oc_sec_cred_t *cr = oc_sec_find_role_cred( + /*start*/ NULL, role_view, authority_view, + /*tag*/ OC_STRING_VIEW_NULL); // ignore tag, we want to serialize + // only the [role,authority] pairs if (cr == NULL) { OC_ERR("no role was found"); return false; @@ -112,9 +127,10 @@ oc_assert_all_roles(const oc_endpoint_t *endpoint, while (roles) { const oc_sec_cred_t *cr = oc_sec_find_role_cred( - /*start*/ NULL, oc_string(roles->role), oc_string(roles->authority), - /*tag*/ NULL); // ignore tag, we want to serialize only the - // [role,authority] pairs + /*start*/ NULL, oc_string_view2(&roles->role), + oc_string_view2(&roles->authority), + /*tag*/ OC_STRING_VIEW_NULL); // ignore tag, we want to serialize only the + // [role,authority] pairs if (cr != NULL) { serialize_role_credential(&roles_array, cr); } diff --git a/api/oc_helpers.c b/api/oc_helpers.c index c9643536c..84ba7175d 100644 --- a/api/oc_helpers.c +++ b/api/oc_helpers.c @@ -143,11 +143,10 @@ oc_set_string(oc_string_t *dst, const char *str, size_t str_len) oc_string_view_t oc_string_view(const char *data, size_t length) { - oc_string_view_t view = { + return (oc_string_view_t){ .data = data, .length = length, }; - return view; } oc_string_view_t diff --git a/api/oc_rep_encode.c b/api/oc_rep_encode.c index e92a46677..e29334f8c 100644 --- a/api/oc_rep_encode.c +++ b/api/oc_rep_encode.c @@ -436,7 +436,7 @@ oc_rep_encoder_write_raw(oc_rep_encoder_t *encoder, const uint8_t *data, memcpy(&encoder->ctx, &prevEncoder, sizeof(prevEncoder)); #else /* OC_DYNAMIC_ALLOCATION */ OC_WRN("Insufficient memory: Increase OC_MAX_APP_DATA_SIZE to " - "accomodate a larger payload(+%zu)", + "accomodate a larger payload(+%lu)", len - remaining); return CborErrorOutOfMemory; #endif /* !OC_DYNAMIC_ALLOCATION */ diff --git a/include/oc_cred.h b/include/oc_cred.h index 128bf189d..e12075e88 100644 --- a/include/oc_cred.h +++ b/include/oc_cred.h @@ -132,8 +132,8 @@ typedef struct oc_sec_creds_t * @return true if security credential matches the filter * @return false otherwise */ -typedef bool (*oc_sec_cred_filter_t)(const oc_sec_cred_t *cred, - void *user_data); +typedef bool (*oc_sec_cred_filter_t)(const oc_sec_cred_t *cred, void *user_data) + OC_NONNULL(1); #ifdef OC_PKI diff --git a/include/oc_role.h b/include/oc_role.h index 8ec3d9840..052b33b9c 100644 --- a/include/oc_role.h +++ b/include/oc_role.h @@ -26,6 +26,7 @@ #include "oc_client_state.h" #include "oc_helpers.h" #include "oc_endpoint.h" +#include "util/oc_compiler.h" #include @@ -55,17 +56,18 @@ oc_role_t *oc_get_all_roles(void); /** * @brief assert the specific role * - * @param role the role + * @param role the role (cannot be NULL) * @param authority the authority - * @param endpoint endpoint identifying the connection - * @param handler the response handler + * @param endpoint endpoint identifying the connection (cannot be NULL) + * @param handler the response handler (cannot be NULL) * @param user_data the user data to be conveyed to the response handler * @return true request was initialized and sent * @return false otherwise */ bool oc_assert_role(const char *role, const char *authority, const oc_endpoint_t *endpoint, - oc_response_handler_t handler, void *user_data); + oc_response_handler_t handler, void *user_data) + OC_NONNULL(1, 3, 4); /** * @brief set automatic role assertion (e.g. for all endpoints with a diff --git a/port/android/Makefile b/port/android/Makefile index 4af6b709f..8a0087f48 100644 --- a/port/android/Makefile +++ b/port/android/Makefile @@ -269,8 +269,9 @@ ifeq ($(PLGD_DEV_TIME),1) endif ifneq ($(SECURE),0) - SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c oc_cred.c oc_csr.c oc_doxm.c oc_entropy.c \ - oc_keypair.c oc_pki.c oc_pstat.c oc_roles.c oc_sdi.c oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) + SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ + oc_cred.c oc_cred_util.c oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_pki.c oc_pstat.c oc_roles.c oc_sdi.c \ + oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS}) MBEDTLS_PATCH_FILE := $(MBEDTLS_DIR)/patched.txt ifeq ($(DYNAMIC),1) diff --git a/port/android/ipadapter.c b/port/android/ipadapter.c index bd3c8b1cd..aa69e6173 100644 --- a/port/android/ipadapter.c +++ b/port/android/ipadapter.c @@ -17,7 +17,9 @@ * ****************************************************************************/ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #if !defined(__ANDROID_API__) || __ANDROID_API__ == 10000 diff --git a/port/android/tcpadapter.c b/port/android/tcpadapter.c index 5c56eda43..b811f560f 100644 --- a/port/android/tcpadapter.c +++ b/port/android/tcpadapter.c @@ -16,7 +16,9 @@ * ****************************************************************************/ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "api/oc_message_internal.h" #include "api/oc_session_events_internal.h" diff --git a/port/arduino/adapter/Makefile b/port/arduino/adapter/Makefile index 9c78b90bb..c8bbc7644 100644 --- a/port/arduino/adapter/Makefile +++ b/port/arduino/adapter/Makefile @@ -39,8 +39,9 @@ ifeq ($(DYNAMIC),1) endif ifeq ($(SECURE),1) - SEC_SRC += $(addprefix $(ROOT_DIR)/security/, oc_acl.c oc_cred.c oc_certs.c oc_certs_generate.c oc_certs_validate.c oc_csr.c oc_doxm.c oc_entropy.c \ - oc_keypair.c oc_pki.c oc_pstat.c oc_roles.c oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) + SEC_SRC += $(addprefix $(ROOT_DIR)/security/, oc_acl.c oc_cred.c oc_cred_util.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ + oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_pki.c oc_pstat.c oc_roles.c oc_security.c oc_sp.c oc_store.c oc_svr.c \ + oc_tls.c) SRC += $(SEC_SRC) SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS}) MBEDTLS_PATCH_FILE := $(MBEDTLS_DIR)/patched.txt diff --git a/port/common/posix/oc_loop_event.c b/port/common/posix/oc_loop_event.c index eb44ada3c..88e2dc9ad 100644 --- a/port/common/posix/oc_loop_event.c +++ b/port/common/posix/oc_loop_event.c @@ -17,7 +17,9 @@ ****************************************************************************/ // make pipe2() available +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "util/oc_features.h" diff --git a/port/common/posix/oc_poll.c b/port/common/posix/oc_poll.c index 32a7bb5ff..a8cdb698a 100644 --- a/port/common/posix/oc_poll.c +++ b/port/common/posix/oc_poll.c @@ -17,7 +17,9 @@ ****************************************************************************/ // make ppoll() available +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "port/common/posix/oc_poll_internal.h" #include "oc_clock_util.h" diff --git a/port/linux/Makefile b/port/linux/Makefile index cd332a244..dff8dddb2 100644 --- a/port/linux/Makefile +++ b/port/linux/Makefile @@ -286,9 +286,9 @@ endif endif ifneq ($(SECURE),0) - SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c oc_cred.c oc_csr.c oc_doxm.c oc_entropy.c \ - oc_keypair.c oc_oscore_engine.c oc_oscore_crypto.c oc_oscore_context.c oc_pki.c oc_pstat.c oc_roles.c oc_sdi.c \ - oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) + SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ + oc_cred.c oc_cred_util.c oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_oscore_engine.c oc_oscore_crypto.c \ + oc_oscore_context.c oc_pki.c oc_pstat.c oc_roles.c oc_sdi.c oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS}) MBEDTLS_PATCH_FILE := $(MBEDTLS_DIR)/patched.txt ifeq ($(DYNAMIC),1) diff --git a/port/linux/ip.c b/port/linux/ip.c index 3984c253e..d4693f105 100644 --- a/port/linux/ip.c +++ b/port/linux/ip.c @@ -16,7 +16,10 @@ * ******************************************************************/ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif + #include "ip.h" #include "port/oc_log_internal.h" #include "util/oc_macros_internal.h" diff --git a/port/linux/tcpsession.c b/port/linux/tcpsession.c index 02f246a35..4c372d0ff 100644 --- a/port/linux/tcpsession.c +++ b/port/linux/tcpsession.c @@ -16,7 +16,9 @@ * ****************************************************************************/ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "util/oc_features.h" diff --git a/port/openthread/Makefile b/port/openthread/Makefile index 12aed7812..35e341853 100644 --- a/port/openthread/Makefile +++ b/port/openthread/Makefile @@ -57,7 +57,7 @@ ifeq ($(MEMORY_TRACE), 1) endif ifeq ($(SECURE),1) - SRC_COMMON += oc_acl.c oc_cred.c oc_doxm.c oc_pstat.c oc_dtls.c oc_svr.c oc_store.c oc_sdi.c + SRC_COMMON += oc_acl.c oc_cred.c oc_cred_util.c oc_doxm.c oc_pstat.c oc_dtls.c oc_svr.c oc_store.c oc_sdi.c SRC_COMMON += memory_buffer_alloc.c CFLAGS += -DOC_SECURITY endif diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj b/port/windows/vs2015/IoTivity-lite.vcxproj index ea259c083..1443878b0 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj +++ b/port/windows/vs2015/IoTivity-lite.vcxproj @@ -461,6 +461,7 @@ + diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj.filters b/port/windows/vs2015/IoTivity-lite.vcxproj.filters index 0fd63643b..c66217321 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj.filters +++ b/port/windows/vs2015/IoTivity-lite.vcxproj.filters @@ -362,6 +362,9 @@ Security + + Security + Security diff --git a/security/oc_certs.c b/security/oc_certs.c index e8b6b35f1..c927bdf0d 100644 --- a/security/oc_certs.c +++ b/security/oc_certs.c @@ -163,6 +163,7 @@ oc_certs_parse_serial_number(const unsigned char *cert, size_t cert_size, int ret = mbedtls_x509_crt_parse(&crt, cert, cert_size); if (ret != 0) { OC_ERR("could not parse the provided cert %d", ret); + mbedtls_x509_crt_free(&crt); return ret; } @@ -197,6 +198,7 @@ oc_certs_parse_private_key(size_t device, const unsigned char *cert, int ret = mbedtls_x509_crt_parse(&crt, cert, cert_size); if (ret != 0) { OC_ERR("could not parse the provided cert %d", ret); + mbedtls_x509_crt_free(&crt); return ret; } @@ -255,6 +257,7 @@ oc_certs_parse_public_key(const unsigned char *cert, size_t cert_size, int ret = mbedtls_x509_crt_parse(&crt, cert, cert_size); if (ret != 0) { OC_ERR("could not parse the provided cert %d", ret); + mbedtls_x509_crt_free(&crt); return -1; } @@ -273,6 +276,7 @@ oc_certs_parse_public_key_to_oc_string(const unsigned char *cert, int ret = mbedtls_x509_crt_parse(&crt, cert, cert_size); if (ret != 0) { OC_ERR("could not parse the provided cert %d", ret); + mbedtls_x509_crt_free(&crt); return -1; } @@ -372,6 +376,7 @@ oc_certs_parse_CN_for_UUID(const unsigned char *cert, size_t cert_size, int ret = mbedtls_x509_crt_parse(&crt, cert, cert_size); if (ret != 0) { OC_ERR("could not parse the provided cert %d", ret); + mbedtls_x509_crt_free(&crt); return false; } @@ -544,6 +549,7 @@ oc_certs_parse_first_role(const unsigned char *cert, size_t cert_size, int ret = mbedtls_x509_crt_parse(&crt, cert, cert_size); if (ret != 0) { OC_ERR("could not parse the provided cert %d", ret); + mbedtls_x509_crt_free(&crt); return false; } diff --git a/security/oc_certs_generate.c b/security/oc_certs_generate.c index 21c1622b4..a3c6625e2 100644 --- a/security/oc_certs_generate.c +++ b/security/oc_certs_generate.c @@ -19,7 +19,8 @@ #include "oc_config.h" -#if defined(OC_SECURITY) && defined(OC_PKI) && defined(OC_DYNAMIC_ALLOCATION) +#if defined(OC_SECURITY) && defined(OC_PKI) && \ + (defined(OC_DYNAMIC_ALLOCATION) || defined(OC_TEST)) #include "port/oc_log_internal.h" #include "security/oc_certs_generate_internal.h" @@ -33,6 +34,7 @@ #include #include #include +#include static bool certs_generate_serial_number(mbedtls_mpi *buffer, size_t size) @@ -632,4 +634,4 @@ oc_certs_generate(const oc_certs_generate_t *data, unsigned char *buffer, return ret; } -#endif /* OC_SECURITY && OC_PKI && OC_DYNAMIC_ALLOCATION */ +#endif /* OC_SECURITY && OC_PKI && (OC_DYNAMIC_ALLOCATION || OC_TEST) */ diff --git a/security/oc_certs_generate_internal.h b/security/oc_certs_generate_internal.h index 02b687329..2c27675bd 100644 --- a/security/oc_certs_generate_internal.h +++ b/security/oc_certs_generate_internal.h @@ -20,7 +20,8 @@ #ifndef OC_CERTS_GENERATE_INTERNAL_H #define OC_CERTS_GENERATE_INTERNAL_H -#if defined(OC_SECURITY) && defined(OC_PKI) && defined(OC_DYNAMIC_ALLOCATION) +#if defined(OC_SECURITY) && defined(OC_PKI) && \ + (defined(OC_DYNAMIC_ALLOCATION) || defined(OC_TEST)) #include "api/c-timestamp/timestamp.h" #include "oc_role.h" @@ -138,6 +139,6 @@ int oc_certs_generate(const oc_certs_generate_t *data, unsigned char *buffer, } #endif -#endif /* OC_SECURITY & OC_PKI && OC_DYNAMIC_ALLOCATION */ +#endif /* OC_SECURITY & OC_PKI && (OC_DYNAMIC_ALLOCATION || OC_TEST) */ #endif /* OC_CERTS_GENERATE_INTERNAL_H */ diff --git a/security/oc_cred.c b/security/oc_cred.c index 001eeaf75..d8eb1bfe5 100644 --- a/security/oc_cred.c +++ b/security/oc_cred.c @@ -20,9 +20,7 @@ #include "api/oc_helpers_internal.h" #include "api/oc_core_res_internal.h" -#include "oc_cred_internal.h" #include "oc_api.h" -#include "oc_base64.h" #include "oc_config.h" #include "oc_core_res.h" #include "oc_store.h" @@ -30,6 +28,8 @@ #include "port/oc_log_internal.h" #include "port/oc_random.h" #include "security/oc_certs_internal.h" +#include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_doxm_internal.h" #include "security/oc_keypair_internal.h" #include "security/oc_pstat_internal.h" @@ -53,9 +53,6 @@ #include OC_MEMB(g_creds, oc_sec_cred_t, OC_MAX_NUM_DEVICES *OC_MAX_NUM_SUBJECTS + 1); -#define OXM_JUST_WORKS "oic.sec.doxm.jw" -#define OXM_RANDOM_DEVICE_PIN "oic.sec.doxm.rdp" -#define OXM_MANUFACTURER_CERTIFICATE "oic.sec.doxm.mfgcert" #ifdef OC_DYNAMIC_ALLOCATION static oc_sec_creds_t *g_devices = NULL; @@ -70,25 +67,13 @@ static oc_string_view_t g_allowed_roles[] = { { } }; #endif /* OC_PKI */ -// https://openconnectivity.org/specs/OCF_Security_Specification_v2.2.5.pdf -// 13.3.3.1 Symmetric key formatting -#define SYMMETRIC_KEY_128BIT_LEN 16 -#define SYMMETRIC_KEY_256BIT_LEN 32 - -static bool -check_symmetric_key_length(size_t key_size) -{ - if (key_size != SYMMETRIC_KEY_128BIT_LEN && - key_size != SYMMETRIC_KEY_256BIT_LEN) { - OC_ERR("oc_cred: invalid PSK length(%zu)", key_size); - return false; - } - return true; -} - oc_sec_creds_t * oc_sec_get_creds(size_t device) { + if (!oc_core_device_is_valid(device)) { + OC_ERR("invalid device index"); + return NULL; + } return &g_devices[device]; } @@ -107,33 +92,21 @@ oc_sec_cred_init(void) } } -oc_sec_cred_t * -oc_sec_get_cred_by_credid(int credid, size_t device) -{ - oc_sec_cred_t *cred = oc_list_head(g_devices[device].creds); - while (cred != NULL) { - if (cred->credid == credid) { - return cred; - } - cred = cred->next; - } - return NULL; -} - static oc_sec_cred_t * -oc_sec_is_existing_cred(int credid, bool roles_resource, - const oc_tls_peer_t *client, size_t device) +cred_get_by_credid(int credid, bool roles_resource, const oc_tls_peer_t *client, + size_t device) { oc_sec_cred_t *cred = NULL; - (void)client; - +#ifdef OC_PKI if (!roles_resource) { cred = (oc_sec_cred_t *)oc_list_head(g_devices[device].creds); - } -#ifdef OC_PKI - else { + } else { cred = oc_sec_roles_get(client); } +#else /* !OC_PKI */ + (void)roles_resource; + (void)client; + cred = (oc_sec_cred_t *)oc_list_head(g_devices[device].creds); #endif /* OC_PKI */ while (cred != NULL) { if (cred->credid == credid) { @@ -144,73 +117,46 @@ oc_sec_is_existing_cred(int credid, bool roles_resource, return cred; } -#ifdef OC_PKI -static bool -oc_sec_role_cred_match_role(const oc_sec_cred_t *cred, const char *role, - size_t role_len, bool skipIfEmpty) -{ - const char *cred_role = oc_string(cred->role.role); - if (role == NULL) { - // skip check or both are NULL - return skipIfEmpty || cred_role == NULL; - } - return role_len == oc_string_len(cred->role.role) && cred_role != NULL && - memcmp(role, cred_role, role_len) == 0; -} - -#ifdef OC_CLIENT -static bool -oc_sec_role_cred_match_authority(const oc_sec_cred_t *cred, - const char *authority, size_t authority_len, - bool skipIfEmpty) +oc_sec_cred_t * +oc_sec_get_cred_by_credid(int credid, size_t device) { - const char *cred_authority = oc_string(cred->role.authority); - if (authority == NULL) { - // skip check or both are NULL - return skipIfEmpty || oc_string(cred->role.authority) == NULL; - } - return authority_len == oc_string_len(cred->role.authority) && - cred_authority != NULL && - memcmp(authority, cred_authority, authority_len) == 0; + return cred_get_by_credid(credid, false, NULL, device); } +#ifdef OC_PKI static bool -oc_sec_role_cred_match_tag(const oc_sec_cred_t *cred, const char *tag, - size_t tag_len, bool skipIfEmpty) +cred_match_attribute(const oc_string_t *cred_attr, oc_string_view_t attr, + bool skipIfEmpty) { - const char *cred_tag = oc_string(cred->tag); - if (tag == NULL) { + const char *cred_cattr = oc_string(*cred_attr); + if (attr.data == NULL) { // skip check or both are NULL - return skipIfEmpty || cred_tag == NULL; + return skipIfEmpty || cred_cattr == NULL; } - - return tag_len == oc_string_len(cred->tag) && cred_tag != NULL && - memcmp(tag, cred_tag, tag_len) == 0; + return cred_cattr != NULL && + oc_string_view_is_equal(oc_string_view2(cred_attr), attr); } +#ifdef OC_CLIENT oc_sec_cred_t * -oc_sec_find_role_cred(oc_sec_cred_t *start, const char *role, - const char *authority, const char *tag) +oc_sec_find_role_cred(oc_sec_cred_t *start, oc_string_view_t role, + oc_string_view_t authority, oc_string_view_t tag) { oc_sec_cred_t *creds = start; - if (!creds) { + if (creds == NULL) { /* Checking only the 0th logical device for Clients */ creds = (oc_sec_cred_t *)oc_list_head(g_devices[0].creds); } - size_t role_len = strlen(role); - size_t authority_len = authority != NULL ? strlen(authority) : 0; - size_t tag_len = tag != NULL ? strlen(tag) : 0; - while (creds) { - if (creds->credtype == OC_CREDTYPE_CERT && - creds->credusage == OC_CREDUSAGE_ROLE_CERT) { - if (oc_sec_role_cred_match_role(creds, role, role_len, false) && - oc_sec_role_cred_match_authority(creds, authority, authority_len, - true) && - oc_sec_role_cred_match_tag(creds, tag, tag_len, true)) { - return creds; - } + for (; creds != NULL; creds = creds->next) { + if (creds->credtype != OC_CREDTYPE_CERT || + creds->credusage != OC_CREDUSAGE_ROLE_CERT) { + continue; + } + if (cred_match_attribute(&creds->role.role, role, false) && + cred_match_attribute(&creds->role.authority, authority, true) && + cred_match_attribute(&creds->tag, tag, true)) { + return creds; } - creds = creds->next; } return NULL; } @@ -223,35 +169,36 @@ get_new_credid(bool roles_resource, const oc_tls_peer_t *client, size_t device) int credid; do { credid = (int)(oc_random_value() >> 1); - } while (oc_sec_is_existing_cred(credid, roles_resource, client, device)); + } while (cred_get_by_credid(credid, roles_resource, client, device)); return credid; } static oc_sec_cred_t * -oc_sec_remove_cred_from_device(oc_sec_cred_t *cred, size_t device) +cred_remove_from_device(const oc_sec_cred_t *cred, size_t device) { return oc_list_remove2(g_devices[device].creds, cred); } -static oc_sec_cred_t * -oc_sec_remove_cred_from_device_by_credid(int credid, size_t device) +oc_sec_cred_t * +oc_sec_cred_remove_from_device_by_credid(int credid, size_t device) { oc_sec_cred_t *cred = oc_sec_get_cred_by_credid(credid, device); - if (cred) { - oc_sec_remove_cred_from_device(cred, device); + if (cred != NULL) { + cred_remove_from_device(cred, device); } return cred; } -static void -oc_sec_free_cred(oc_sec_cred_t *cred) +void +oc_sec_cred_free(oc_sec_cred_t *cred) { - if (oc_string_len(cred->role.role) > 0) { #if defined(OC_PKI) && defined(OC_CLIENT) + if (!oc_string_is_empty(&cred->role.role)) { oc_sec_role_cred_remove(oc_string_view2(&cred->role.role), oc_string_view2(&cred->role.authority)); -#endif /* OC_PKI && OC_CLIENT */ } +#endif /* OC_PKI && OC_CLIENT */ + oc_free_string(&cred->role.role); oc_free_string(&cred->role.authority); oc_free_string(&cred->privatedata.data); @@ -286,8 +233,8 @@ oc_sec_free_cred(oc_sec_cred_t *cred) void oc_sec_remove_cred(oc_sec_cred_t *cred, size_t device) { - oc_sec_remove_cred_from_device(cred, device); - oc_sec_free_cred(cred); + cred_remove_from_device(cred, device); + oc_sec_cred_free(cred); } bool @@ -314,6 +261,39 @@ oc_sec_cred_clear(size_t device, oc_sec_cred_filter_t filter, void *user_data) } } +oc_sec_cred_t * +oc_cred_find_by_subject(const char *subjectuuid, size_t device) +{ + assert(oc_core_device_is_valid(device)); + + oc_uuid_t uuid; + if (subjectuuid[0] == '*') { + memset(&uuid, 0, sizeof(oc_uuid_t)); + uuid.id[0] = '*'; + } else { + oc_str_to_uuid(subjectuuid, &uuid); + } + + for (oc_sec_cred_t *cred = oc_list_head(g_devices[device].creds); + cred != NULL; cred = cred->next) { + if (memcmp(cred->subjectuuid.id, uuid.id, sizeof(uuid.id)) == 0) { + return cred; + } + } + return NULL; +} + +bool +oc_cred_remove_by_subject(const char *subjectuuid, size_t device) +{ + oc_sec_cred_t *cred = oc_cred_find_by_subject(subjectuuid, device); + if (cred == NULL) { + return false; + } + oc_sec_remove_cred(cred, device); + return true; +} + void oc_sec_cred_default(size_t device) { @@ -323,7 +303,7 @@ oc_sec_cred_default(size_t device) } void -oc_sec_cred_free(void) +oc_sec_cred_deinit(void) { for (size_t device = 0; device < oc_core_get_num_devices(); device++) { oc_sec_cred_clear(device, NULL, NULL); @@ -331,6 +311,7 @@ oc_sec_cred_free(void) #ifdef OC_DYNAMIC_ALLOCATION if (g_devices != NULL) { free(g_devices); + g_devices = NULL; } #endif /* OC_DYNAMIC_ALLOCATION */ } @@ -405,8 +386,7 @@ check_role_assertion(oc_sec_cred_t *cred) if (oc_string_len(cred->role.role) >= reserved.length && memcmp(oc_string(cred->role.role), reserved.data, reserved.length) == 0) { for (size_t i = 0; i < OC_ARRAY_SIZE(g_allowed_roles); i++) { - if (oc_sec_role_cred_match_role(cred, g_allowed_roles[i].data, - g_allowed_roles[i].length, false)) { + if (cred_match_attribute(&cred->role.role, g_allowed_roles[i], false)) { return true; } } @@ -473,74 +453,6 @@ oc_sec_verify_role_cred(const oc_tls_peer_t *client, #endif /* OC_PKI */ -static bool -oc_sec_is_equal_cred_data(oc_cred_data_t creddata, const uint8_t *data, - size_t data_size) -{ - return (oc_string_len(creddata.data) == data_size) && - (data == NULL || - (memcmp(oc_string(creddata.data), data, data_size) == 0)); -} - -static bool -oc_sec_is_equal_cred_tag(const oc_string_t *credtag, oc_string_view_t tag) -{ - size_t credtag_size = credtag->size; - size_t tag_size = tag.data != NULL ? tag.length + 1 : 0; - return (credtag_size == tag_size) && - ((tag.data == NULL) || - (memcmp(oc_string(*credtag), tag.data, credtag_size) == 0)); -} - -static bool -oc_sec_cred_set_subject(oc_sec_credusage_t credusage, const char *subjectuuid, - oc_uuid_t *subject) -{ - if (!subjectuuid) { - if (credusage != OC_CREDUSAGE_ROLE_CERT) { - return false; - } else { - subject->id[0] = '*'; - } - } else { - if (subjectuuid[0] == '*') { - subject->id[0] = '*'; - } else { - oc_str_to_uuid(subjectuuid, subject); - } - } - return true; -} - -static bool -oc_sec_is_duplicate_cred(const oc_sec_cred_t *cred, oc_sec_credtype_t credtype, - oc_sec_credusage_t credusage, oc_uuid_t subject, - size_t privatedata_size, const uint8_t *privatedata, - size_t publicdata_size, const uint8_t *publicdata, - oc_string_view_t tag) -{ - if ((cred->credtype != credtype) || - !oc_uuid_is_equal(cred->subjectuuid, subject) || - !oc_sec_is_equal_cred_data(cred->privatedata, privatedata, - privatedata_size) || - !oc_sec_is_equal_cred_tag(&cred->tag, tag)) { - return false; - } - -#ifdef OC_PKI - if ((cred->credusage != credusage) || - !oc_sec_is_equal_cred_data(cred->publicdata, publicdata, - publicdata_size)) { - return false; - } -#else /* !OC_PKI */ - (void)credusage; - (void)publicdata; - (void)publicdata_size; -#endif /* OC_PKI */ - return true; -} - #ifdef OC_PKI static const oc_ecdsa_keypair_t * oc_sec_get_valid_ecdsa_keypair(size_t device, size_t public_key_len, @@ -604,34 +516,6 @@ cred_deallocate(oc_sec_cred_t *cred, size_t device, bool roles_resource, oc_sec_remove_cred(cred, device); } -static int -cred_set_privatedata(oc_sec_cred_t *cred, const uint8_t *data, size_t data_size, - oc_sec_encoding_t encoding) -{ - if (cred->credtype == OC_CREDTYPE_PSK) { - if (encoding == OC_ENCODING_BASE64) { - if (data_size > 64) { - return -1; - } - uint8_t key[64]; - memcpy(key, data, data_size); - int key_size = oc_base64_decode(key, data_size); - if (key_size < 0 || !check_symmetric_key_length((size_t)key_size)) { - return -1; - } - oc_new_string(&cred->privatedata.data, (const char *)key, key_size); - cred->privatedata.encoding = OC_ENCODING_RAW; - return 0; - } - if (!check_symmetric_key_length(data_size)) { - return -1; - } - } - oc_new_string(&cred->privatedata.data, (const char *)data, data_size); - cred->privatedata.encoding = encoding; - return 0; -} - typedef struct cred_create_t { const oc_uuid_t *subject; @@ -690,11 +574,12 @@ cred_create(const cred_create_t *create) cred->credtype = create->credtype; /* privatedata */ if (create->privatedata.data != NULL && create->privatedata.size > 0) { - if (cred_set_privatedata(cred, create->privatedata.data, - create->privatedata.size, - create->privatedata.encoding) != 0) { + if (!oc_cred_set_privatedata(cred, create->privatedata.data, + create->privatedata.size, + create->privatedata.encoding)) { cred_deallocate(cred, create->device, create->roles_resource, create->client); + return NULL; } } else { #ifdef OC_PKI @@ -743,9 +628,8 @@ sec_match_cred_with_publicdata(const oc_sec_cred_t *cred, return (cred->credusage == credusage) && /* Trying to add a duplicate certificate chain, so ignore */ (publicdata.size > 0 && - oc_sec_is_equal_cred_data(cred->publicdata, publicdata.data, - publicdata.size) && - oc_sec_is_equal_cred_tag(&cred->tag, tag)); + oc_cred_data_is_equal_to_encoded_data(cred->publicdata, publicdata) && + oc_cred_has_tag(cred, tag)); } #endif /* OC_PKI */ @@ -782,7 +666,9 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, oc_uuid_t subject; memset(&subject, 0, sizeof(oc_uuid_t)); - oc_sec_cred_set_subject(credusage, subjectuuid, &subject); + if (!oc_sec_cred_set_subject(subjectuuid, credusage, &subject)) { + goto add_new_cred_error; + } #ifdef OC_PKI const oc_ecdsa_keypair_t *kp = NULL; @@ -796,24 +682,22 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #endif /* OC_PKI */ const oc_sec_cred_t *existing = - oc_sec_is_existing_cred(credid, roles_resource, client, device); + cred_get_by_credid(credid, roles_resource, client, device); if (existing) { if (!roles_resource) { /* skip duplicate cred, if one exists. */ - if (oc_sec_is_duplicate_cred(existing, credtype, credusage, subject, - privatedata.size, privatedata.data, - publicdata.size, publicdata.data, tag)) { + if (oc_cred_is_duplicate(existing, credtype, subject, tag, privatedata, + publicdata, credusage)) { #ifdef OC_PKI oc_free_string(&public_key); #endif /* OC_PKI */ return credid; + } + if (new_cred_data) { + new_cred_data->replaced_cred = + oc_sec_cred_remove_from_device_by_credid(credid, device); } else { - if (new_cred_data) { - new_cred_data->replaced_cred = - oc_sec_remove_cred_from_device_by_credid(credid, device); - } else { - oc_sec_remove_cred_by_credid(credid, device); - } + oc_sec_remove_cred_by_credid(credid, device); } } #ifdef OC_PKI @@ -835,7 +719,7 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, if (new_cred_data) { oc_assert(new_cred_data->replaced_cred == NULL); new_cred_data->replaced_cred = - oc_sec_remove_cred_from_device(cred, device); + cred_remove_from_device(cred, device); } else { oc_sec_remove_cred(cred, device); } @@ -860,9 +744,9 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, oc_sec_cred_t *roles = oc_sec_roles_get(client); while (roles) { /* Trying to add a duplicate role credential, so ignore */ - if (oc_sec_is_equal_cred_data(roles->publicdata, publicdata.data, - publicdata.size) && - oc_sec_is_equal_cred_tag(&roles->tag, tag)) { + if (oc_cred_data_is_equal_to_encoded_data(roles->publicdata, + publicdata) && + oc_cred_has_tag(roles, tag)) { oc_free_string(&public_key); return roles->credid; } @@ -909,7 +793,7 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, } #if defined(OC_PKI) && defined(OC_CLIENT) if (!roles_resource && credusage == OC_CREDUSAGE_ROLE_CERT && - oc_string_len(cred->role.role) > 0) { + !oc_string_is_empty(&cred->role.role)) { oc_sec_role_cred_add_or_get(oc_string_view2(&cred->role.role), oc_string_view2(&cred->role.authority)); } @@ -925,87 +809,20 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, oc_free_string(&public_key); #endif /* OC_PKI */ if (new_cred_data && new_cred_data->replaced_cred) { - oc_sec_free_cred(new_cred_data->replaced_cred); + oc_sec_cred_free(new_cred_data->replaced_cred); new_cred_data->replaced_cred = NULL; } return -1; } -const char * -oc_cred_credtype_string(oc_sec_credtype_t credtype) -{ - if (credtype == 1) { - return "Symmetric pair-wise key"; - } else if (credtype == 8) { - return "Asymmetric signing key with certificate"; - } - return "Unknown"; -} - -#ifdef OC_PKI - -oc_string_view_t -oc_cred_credusage_to_string(oc_sec_credusage_t credusage) -{ - switch (credusage) { - case OC_CREDUSAGE_TRUSTCA: - return oc_string_view(OC_CREDUSAGE_TRUSTCA_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_TRUSTCA_STR)); - case OC_CREDUSAGE_IDENTITY_CERT: - return oc_string_view(OC_CREDUSAGE_IDENTITY_CERT_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_IDENTITY_CERT_STR)); - case OC_CREDUSAGE_ROLE_CERT: - return oc_string_view(OC_CREDUSAGE_ROLE_CERT_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_ROLE_CERT_STR)); - case OC_CREDUSAGE_MFG_TRUSTCA: - return oc_string_view(OC_CREDUSAGE_MFG_TRUSTCA_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_TRUSTCA_STR)); - case OC_CREDUSAGE_MFG_CERT: - return oc_string_view(OC_CREDUSAGE_MFG_CERT_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_CERT_STR)); - default: - break; - } - return oc_string_view("None", OC_CHAR_ARRAY_LEN("None")); -} - -const char * -oc_cred_read_credusage(oc_sec_credusage_t credusage) -{ - oc_string_view_t cu = oc_cred_credusage_to_string(credusage); - return cu.data; -} -#endif /* OC_PKI */ - -oc_string_view_t -oc_cred_encoding_to_string(oc_sec_encoding_t encoding) -{ - switch (encoding) { - case OC_ENCODING_BASE64: - return oc_string_view(OC_ENCODING_BASE64_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_BASE64_STR)); - case OC_ENCODING_RAW: - return oc_string_view(OC_ENCODING_RAW_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR)); -#ifdef OC_PKI - case OC_ENCODING_PEM: - return oc_string_view(OC_ENCODING_PEM_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_PEM_STR)); -#endif /* OC_PKI */ - case OC_ENCODING_HANDLE: - return oc_string_view(OC_ENCODING_HANDLE_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_HANDLE_STR)); - default: - break; - } - return oc_string_view("Unknown", OC_CHAR_ARRAY_LEN("Unknown")); -} - -const char * -oc_cred_read_encoding(oc_sec_encoding_t encoding) +int +oc_sec_add_new_psk_cred(size_t device, const char *subjectuuid, + oc_sec_encoded_data_t privatedata, oc_string_view_t tag) { - oc_string_view_t enc = oc_cred_encoding_to_string(encoding); - return enc.data; + return oc_sec_add_new_cred( + device, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, subjectuuid, + privatedata, (oc_sec_encoded_data_t){ NULL, 0, OC_ENCODING_UNSUPPORTED }, + OC_STRING_VIEW_NULL, OC_STRING_VIEW_NULL, tag, NULL); } void @@ -1036,11 +853,11 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, } /* roleid */ if ((to_storage || cr->credtype == OC_CREDTYPE_PSK) && - oc_string_len(cr->role.role) > 0) { + !oc_string_is_empty(&cr->role.role)) { oc_rep_set_object(creds, roleid); oc_rep_set_text_string_v1(roleid, role, oc_string(cr->role.role), oc_string_len(cr->role.role)); - if (oc_string_len(cr->role.authority) > 0) { + if (!oc_string_is_empty(&cr->role.authority)) { oc_rep_set_text_string_v1(roleid, authority, oc_string(cr->role.authority), oc_string_len(cr->role.authority)); @@ -1113,7 +930,7 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, credusage_string.length); } /* publicdata */ - if (oc_string_len(cr->publicdata.data) > 0) { + if (!oc_string_is_empty(&cr->publicdata.data)) { oc_rep_set_object(creds, publicdata); if (cr->publicdata.encoding == OC_ENCODING_PEM) { oc_rep_set_text_string_v1(publicdata, data, @@ -1134,7 +951,7 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, } if (to_storage) { oc_rep_set_boolean(creds, owner_cred, cr->owner_cred); - if (oc_string_len(cr->tag) > 0) { + if (!oc_string_is_empty(&cr->tag)) { oc_rep_set_text_string_v1(creds, tag, oc_string(cr->tag), oc_string_len(cr->tag)); } @@ -1153,42 +970,6 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, #ifdef OC_PKI -oc_sec_credusage_t -oc_cred_usage_from_string(const char *str, size_t str_len) -{ - if (str_len == OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_TRUSTCA_STR) && - memcmp(OC_CREDUSAGE_TRUSTCA_STR, str, str_len) == 0) { - return OC_CREDUSAGE_TRUSTCA; - } - if (str_len == OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_IDENTITY_CERT_STR) && - memcmp(OC_CREDUSAGE_IDENTITY_CERT_STR, str, str_len) == 0) { - return OC_CREDUSAGE_IDENTITY_CERT; - } - if (str_len == OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_ROLE_CERT_STR) && - memcmp(OC_CREDUSAGE_ROLE_CERT_STR, str, str_len) == 0) { - return OC_CREDUSAGE_ROLE_CERT; - } - if (str_len == OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_TRUSTCA_STR) && - memcmp(OC_CREDUSAGE_MFG_TRUSTCA_STR, str, str_len) == 0) { - return OC_CREDUSAGE_MFG_TRUSTCA; - } - if (str_len == OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_CERT_STR) && - memcmp(OC_CREDUSAGE_MFG_CERT_STR, str, str_len) == 0) { - return OC_CREDUSAGE_MFG_CERT; - } - return OC_CREDUSAGE_NULL; -} - -oc_sec_credusage_t -oc_cred_parse_credusage(const oc_string_t *credusage_string) -{ - if (oc_string_len(*credusage_string) == 0) { - return OC_CREDUSAGE_NULL; - } - return oc_cred_usage_from_string(oc_string(*credusage_string), - oc_string_len(*credusage_string)); -} - static bool oc_cred_parse_certificate(const oc_sec_cred_t *cred, mbedtls_x509_crt *crt) { @@ -1208,6 +989,7 @@ oc_cred_parse_certificate(const oc_sec_cred_t *cred, mbedtls_x509_crt *crt) int ret = mbedtls_x509_crt_parse(crt, cert, cert_size); if (ret < 0) { OC_ERR("failed to parse certificate: %d", ret); + mbedtls_x509_crt_free(crt); return false; } return true; @@ -1304,40 +1086,6 @@ oc_cred_verify_certificate_chain(const oc_sec_cred_t *cred, #endif /* OC_PKI */ -oc_sec_encoding_t -oc_cred_encoding_from_string(const char *str, size_t str_len) -{ - if (str_len == OC_CHAR_ARRAY_LEN(OC_ENCODING_BASE64_STR) && - memcmp(OC_ENCODING_BASE64_STR, str, str_len) == 0) { - return OC_ENCODING_BASE64; - } - if (str_len == OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR) && - memcmp(OC_ENCODING_RAW_STR, str, str_len) == 0) { - return OC_ENCODING_RAW; - } - if (str_len == OC_CHAR_ARRAY_LEN(OC_ENCODING_HANDLE_STR) && - memcmp(OC_ENCODING_HANDLE_STR, str, str_len) == 0) { - return OC_ENCODING_HANDLE; - } -#ifdef OC_PKI - if (str_len == OC_CHAR_ARRAY_LEN(OC_ENCODING_PEM_STR) && - memcmp(OC_ENCODING_PEM_STR, str, str_len) == 0) { - return OC_ENCODING_PEM; - } -#endif /* OC_PKI */ - return OC_ENCODING_UNSUPPORTED; -} - -oc_sec_encoding_t -oc_cred_parse_encoding(const oc_string_t *encoding_string) -{ - if (oc_string_len(*encoding_string) == 0) { - return OC_ENCODING_UNSUPPORTED; - } - return oc_cred_encoding_from_string(oc_string(*encoding_string), - oc_string_len(*encoding_string)); -} - #ifdef OC_OSCORE static bool is_valid_oscore_id(const char *id, size_t id_len) @@ -1657,7 +1405,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, device, sid, rid, ssn, desc, cr, from_storage); if (!oscore_ctx) { if (add_cred_data.replaced_cred) { - oc_sec_free_cred(add_cred_data.replaced_cred); + oc_sec_cred_free(add_cred_data.replaced_cred); } return false; } @@ -1684,7 +1432,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, } } if (add_cred_data.replaced_cred) { - oc_sec_free_cred(add_cred_data.replaced_cred); + oc_sec_cred_free(add_cred_data.replaced_cred); } } creds_array = creds_array->next; @@ -1704,29 +1452,6 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, return true; } -bool -oc_cred_remove_subject(const char *subjectuuid, size_t device) -{ - oc_uuid_t _subjectuuid; - if (subjectuuid[0] == '*') { - memset(&_subjectuuid, 0, sizeof(oc_uuid_t)); - _subjectuuid.id[0] = '*'; - } else { - oc_str_to_uuid(subjectuuid, &_subjectuuid); - } - oc_sec_cred_t *cred = oc_list_head(g_devices[device].creds); - while (cred != NULL) { - oc_sec_cred_t *next = cred->next; - if (memcmp(cred->subjectuuid.id, _subjectuuid.id, - sizeof(_subjectuuid.id)) == 0) { - oc_sec_remove_cred(cred, device); - return true; - } - cred = next; - } - return false; -} - int oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, const oc_endpoint_t *endpoint, @@ -1765,22 +1490,23 @@ oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, oc_alloc_string(&owner->privatedata.data, uuid_size + 1); if (doxm->oxmsel == OC_OXMTYPE_JW) { success = oc_sec_derive_owner_psk( - endpoint, (const uint8_t *)OXM_JUST_WORKS, strlen(OXM_JUST_WORKS), - doxm->deviceuuid.id, uuid_size, owner->subjectuuid.id, uuid_size, + endpoint, (const uint8_t *)OC_OXMTYPE_JW_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_JW_STR), doxm->deviceuuid.id, uuid_size, + owner->subjectuuid.id, uuid_size, oc_cast(owner->privatedata.data, uint8_t), uuid_size); } else if (doxm->oxmsel == OC_OXMTYPE_RDP) { success = oc_sec_derive_owner_psk( - endpoint, (const uint8_t *)OXM_RANDOM_DEVICE_PIN, - strlen(OXM_RANDOM_DEVICE_PIN), doxm->deviceuuid.id, uuid_size, + endpoint, (const uint8_t *)OC_OXMTYPE_RDP_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_RDP_STR), doxm->deviceuuid.id, uuid_size, owner->subjectuuid.id, uuid_size, oc_cast(owner->privatedata.data, uint8_t), uuid_size); } #ifdef OC_PKI else if (doxm->oxmsel == OC_OXMTYPE_MFG_CERT) { success = oc_sec_derive_owner_psk( - endpoint, (const uint8_t *)OXM_MANUFACTURER_CERTIFICATE, - strlen(OXM_MANUFACTURER_CERTIFICATE), doxm->deviceuuid.id, uuid_size, - owner->subjectuuid.id, uuid_size, + endpoint, (const uint8_t *)OC_OXMTYPE_MFG_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_MFG_CERT_STR), doxm->deviceuuid.id, + uuid_size, owner->subjectuuid.id, uuid_size, oc_cast(owner->privatedata.data, uint8_t), uuid_size); } #endif /* OC_PKI */ diff --git a/security/oc_cred_internal.h b/security/oc_cred_internal.h index 824bd2ac4..1b5172ba0 100644 --- a/security/oc_cred_internal.h +++ b/security/oc_cred_internal.h @@ -42,20 +42,11 @@ typedef struct ///< credential replaced a previously existing one } oc_sec_add_new_cred_data_t; -#define OC_ENCODING_BASE64_STR "oic.sec.encoding.base64" -#define OC_ENCODING_RAW_STR "oic.sec.encoding.raw" -#define OC_ENCODING_HANDLE_STR "oic.sec.encoding.handle" -#ifdef OC_PKI -#define OC_ENCODING_PEM_STR "oic.sec.encoding.pem" - -#define OC_CREDUSAGE_TRUSTCA_STR "oic.sec.cred.trustca" -#define OC_CREDUSAGE_IDENTITY_CERT_STR "oic.sec.cred.cert" -#define OC_CREDUSAGE_ROLE_CERT_STR "oic.sec.cred.rolecert" -#define OC_CREDUSAGE_MFG_TRUSTCA_STR "oic.sec.cred.mfgtrustca" -#define OC_CREDUSAGE_MFG_CERT_STR "oic.sec.cred.mfgcert" - -#endif /* OC_PKI */ - +/** + * Encoded data (in case the data is in the PEM string format, the string must + * contain the nul-terminator, but the size value must be the length without + * nul-terminator. This is required for convertibility with oc_cred_data_t.) + * */ typedef struct { const uint8_t *data; @@ -72,9 +63,29 @@ int oc_sec_add_new_cred(size_t device, bool roles_resource, oc_string_view_t authority, oc_string_view_t tag, oc_sec_add_new_cred_data_t *new_cred_data); +/** Convenience wrapper over oc_sec_add_new_cred to create a new PSK credential + */ +int oc_sec_add_new_psk_cred(size_t device, const char *subjectuuid, + oc_sec_encoded_data_t privatedata, + oc_string_view_t tag); + +/** Deallocate given credential */ +void oc_sec_cred_free(oc_sec_cred_t *cred) OC_NONNULL(); + +/** + * @brief Remove credential from device + * + * @param credid credential ID + * @param device device index + * @return NULL if credential with given ID was not found on device + * @return oc_sec_cred_t * if credential was found and removed + */ +oc_sec_cred_t *oc_sec_cred_remove_from_device_by_credid(int credid, + size_t device); + void oc_sec_cred_default(size_t device); void oc_sec_cred_init(void); -void oc_sec_cred_free(void); +void oc_sec_cred_deinit(void); void oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, bool to_storage); bool oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, @@ -83,19 +94,6 @@ bool oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, oc_sec_on_apply_cred_cb_t on_apply_cred_cb, void *on_apply_cred_data); -/** Convert encoding to oc_string_view_t */ -oc_string_view_t oc_cred_encoding_to_string(oc_sec_encoding_t encoding); - -/** - * @brief Parse cred encoding from string - * - * @param str string (cannot be NULL) - * @param str_len length of \p str - * @return oc_sec_encoding_t parsed encoding - */ -oc_sec_encoding_t oc_cred_encoding_from_string(const char *str, size_t str_len) - OC_NONNULL(); - /** * @brief Allocate and initialize a new credential and append it to global * list of device credentials. @@ -112,8 +110,12 @@ oc_sec_cred_t *oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, oc_sec_credusage_t credusage, size_t device); +/** Find first credential with matching subject UUID */ +oc_sec_cred_t *oc_cred_find_by_subject(const char *subjectuuid, size_t device) + OC_NONNULL(); + /** - * @brief Remove and deallocate credential with matching subject uuid from the + * @brief Remove and deallocate credential with matching subject UUID from the * list of credentials for given device. * * @param subjectuuid subject uuid (cannot be NULL) @@ -121,7 +123,8 @@ oc_sec_cred_t *oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, * @return true credential was found and removed * @return false otherwise */ -bool oc_cred_remove_subject(const char *subjectuuid, size_t device); +bool oc_cred_remove_by_subject(const char *subjectuuid, size_t device) + OC_NONNULL(); /** * @brief Find credential with matching subject uuid from the list of @@ -162,16 +165,17 @@ oc_sec_cred_t *oc_sec_find_cred(oc_sec_cred_t *start, * * @param start Starting position of the search (if NULL is used then the search * starts from the head of the list) - * @param role role to match (cannot be NULL) - * @param authority authority to match (if NULL then the authority values won't + * @param role role to match + * @param authority authority to match (if empty then the authority values won't * be compared) - * @param tag tag to match (if NULL then the tag values won't be - * compared) + * @param tag tag to match (if empty then the tag values won't be compared) * @return oc_sec_cred_t* matching credential * @return NULL if no matching credential was found */ -oc_sec_cred_t *oc_sec_find_role_cred(oc_sec_cred_t *start, const char *role, - const char *authority, const char *tag); +oc_sec_cred_t *oc_sec_find_role_cred(oc_sec_cred_t *start, + oc_string_view_t role, + oc_string_view_t authority, + oc_string_view_t tag); /** * @brief Create roles (/oic/sec/cred) resource for given device. @@ -180,23 +184,6 @@ oc_sec_cred_t *oc_sec_find_role_cred(oc_sec_cred_t *start, const char *role, */ void oc_sec_cred_create_resource(size_t device); -#ifdef OC_PKI - -/** @brief Convert credusage to oc_string_view_t */ -oc_string_view_t oc_cred_credusage_to_string(oc_sec_credusage_t credusage); - -/** - * @brief Parse cred usage from string - * - * @param str string (cannot be NULL) - * @param str_len length of \p str - * @return oc_sec_credusage_t parsed usage - */ -oc_sec_credusage_t oc_cred_usage_from_string(const char *str, size_t str_len) - OC_NONNULL(); - -#endif /* OC_PKI */ - #ifdef __cplusplus } #endif diff --git a/security/oc_cred_util.c b/security/oc_cred_util.c new file mode 100644 index 000000000..727881042 --- /dev/null +++ b/security/oc_cred_util.c @@ -0,0 +1,370 @@ +/**************************************************************************** + * + * Copyright (c) 2016-2019 Intel Corporation + * Copyright (c) 2023 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifdef OC_SECURITY + +#include "oc_base64.h" +#include "oc_cred.h" +#include "port/oc_log_internal.h" +#include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" +#include "util/oc_mmem_internal.h" + +#include + +const char * +oc_cred_credtype_string(oc_sec_credtype_t credtype) +{ + if (credtype == OC_CREDTYPE_PSK) { + return OC_CREDTYPE_PSK_STR; + } + if (credtype == OC_CREDTYPE_CERT) { + return OC_CREDTYPE_CERT_STR; + } + return "Unknown"; +} + +oc_string_view_t +oc_cred_encoding_to_string(oc_sec_encoding_t encoding) +{ + switch (encoding) { + case OC_ENCODING_BASE64: + return oc_string_view(OC_ENCODING_BASE64_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_BASE64_STR)); + case OC_ENCODING_RAW: + return oc_string_view(OC_ENCODING_RAW_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR)); +#ifdef OC_PKI + case OC_ENCODING_PEM: + return oc_string_view(OC_ENCODING_PEM_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_PEM_STR)); +#endif /* OC_PKI */ + case OC_ENCODING_HANDLE: + return oc_string_view(OC_ENCODING_HANDLE_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_HANDLE_STR)); + default: + break; + } + return oc_string_view("Unknown", OC_CHAR_ARRAY_LEN("Unknown")); +} + +const char * +oc_cred_read_encoding(oc_sec_encoding_t encoding) +{ + oc_string_view_t enc = oc_cred_encoding_to_string(encoding); + return enc.data; +} + +oc_sec_encoding_t +oc_cred_encoding_from_string(const char *str, size_t str_len) +{ + oc_string_view_t view = oc_string_view(str, str_len); + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_ENCODING_BASE64_STR), view)) { + return OC_ENCODING_BASE64; + } + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_ENCODING_RAW_STR), view)) { + return OC_ENCODING_RAW; + } + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_ENCODING_HANDLE_STR), view)) { + return OC_ENCODING_HANDLE; + } +#ifdef OC_PKI + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_ENCODING_PEM_STR), view)) { + return OC_ENCODING_PEM; + } +#endif /* OC_PKI */ + return OC_ENCODING_UNSUPPORTED; +} + +oc_sec_encoding_t +oc_cred_parse_encoding(const oc_string_t *encoding_string) +{ + return oc_cred_encoding_from_string(oc_string(*encoding_string), + oc_string_len(*encoding_string)); +} + +bool +oc_cred_data_is_equal_to_encoded_data(oc_cred_data_t cd, + oc_sec_encoded_data_t sed) +{ + if (cd.encoding != sed.encoding) { + return false; + } + size_t cddata_size = + cd.encoding == OC_ENCODING_PEM ? oc_string_len(cd.data) : cd.data.size; + return cddata_size == sed.size && + (sed.data == NULL || + (memcmp(oc_string(cd.data), sed.data, sed.size) == 0)); +} + +bool +oc_cred_has_tag(const oc_sec_cred_t *cred, oc_string_view_t tag) +{ + oc_string_view_t credtag = oc_string_view2(&cred->tag); + return oc_string_view_is_equal(credtag, tag); +} + +bool +oc_cred_is_duplicate(const oc_sec_cred_t *cred, oc_sec_credtype_t credtype, + oc_uuid_t subject, oc_string_view_t tag, + oc_sec_encoded_data_t privatedata, + oc_sec_encoded_data_t publicdata, + oc_sec_credusage_t credusage) +{ + if ((cred->credtype != credtype) || + !oc_uuid_is_equal(cred->subjectuuid, subject) || + !oc_cred_data_is_equal_to_encoded_data(cred->privatedata, privatedata) || + !oc_cred_has_tag(cred, tag)) { + return false; + } + +#ifdef OC_PKI + if ((cred->credusage != credusage) || + !oc_cred_data_is_equal_to_encoded_data(cred->publicdata, publicdata)) { + return false; + } +#else /* !OC_PKI */ + (void)publicdata; + (void)credusage; +#endif /* OC_PKI */ + return true; +} + +void +oc_cred_iterate(const oc_list_t creds, oc_cred_iterate_fn_t iterate, + void *iterate_data) +{ + for (oc_sec_cred_t *cred = (oc_sec_cred_t *)oc_list_head(creds); cred != NULL; + cred = cred->next) { + // simplifying expectation -> the creds list is not modified by the + // iteration function or it exits immediately after modification + if (!iterate(cred, iterate_data)) { + break; + } + } +} + +bool +oc_sec_cred_set_subject(const char *subjectuuid, oc_sec_credusage_t credusage, + oc_uuid_t *subject) +{ + if (subjectuuid == NULL) { + if (credusage == OC_CREDUSAGE_ROLE_CERT) { + subject->id[0] = '*'; + return true; + } + return false; + } + + if (subjectuuid[0] == '*') { + subject->id[0] = '*'; + } else { + oc_str_to_uuid(subjectuuid, subject); + } + return true; +} + +static bool +cred_check_symmetric_key_length(size_t key_size) +{ +// https://openconnectivity.org/specs/OCF_Security_Specification_v2.2.5.pdf +// 13.3.3.1 Symmetric key formatting +#define SYMMETRIC_KEY_128BIT_LEN 16 +#define SYMMETRIC_KEY_256BIT_LEN 32 + if (key_size != SYMMETRIC_KEY_128BIT_LEN && + key_size != SYMMETRIC_KEY_256BIT_LEN) { + OC_ERR("oc_cred: invalid PSK length(%zu)", key_size); + return false; + } + return true; +#undef SYMMETRIC_KEY_256BIT_LEN +#undef SYMMETRIC_KEY_128BIT_LEN +} + +bool +oc_cred_set_privatedata(oc_sec_cred_t *cred, const uint8_t *data, + size_t data_size, oc_sec_encoding_t encoding) +{ + if (cred->credtype == OC_CREDTYPE_PSK) { + if (encoding == OC_ENCODING_BASE64) { + if (data_size > 64) { + return false; + } + uint8_t key[64]; + memcpy(key, data, data_size); + int key_size = oc_base64_decode(key, data_size); + if (key_size < 0 || !cred_check_symmetric_key_length((size_t)key_size)) { + return false; + } + oc_new_string(&cred->privatedata.data, (const char *)key, key_size); + cred->privatedata.encoding = OC_ENCODING_RAW; + return true; + } + if (!cred_check_symmetric_key_length(data_size)) { + return false; + } + } + oc_new_string(&cred->privatedata.data, (const char *)data, data_size); + cred->privatedata.encoding = encoding; + return true; +} + +#ifdef OC_PKI + +oc_string_view_t +oc_cred_credusage_to_string(oc_sec_credusage_t credusage) +{ + switch (credusage) { + case OC_CREDUSAGE_TRUSTCA: + return oc_string_view(OC_CREDUSAGE_TRUSTCA_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_TRUSTCA_STR)); + case OC_CREDUSAGE_IDENTITY_CERT: + return oc_string_view(OC_CREDUSAGE_IDENTITY_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_IDENTITY_CERT_STR)); + case OC_CREDUSAGE_ROLE_CERT: + return oc_string_view(OC_CREDUSAGE_ROLE_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_ROLE_CERT_STR)); + case OC_CREDUSAGE_MFG_TRUSTCA: + return oc_string_view(OC_CREDUSAGE_MFG_TRUSTCA_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_TRUSTCA_STR)); + case OC_CREDUSAGE_MFG_CERT: + return oc_string_view(OC_CREDUSAGE_MFG_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_CERT_STR)); + default: + break; + } + return oc_string_view("None", OC_CHAR_ARRAY_LEN("None")); +} + +const char * +oc_cred_read_credusage(oc_sec_credusage_t credusage) +{ + oc_string_view_t cu = oc_cred_credusage_to_string(credusage); + return cu.data; +} + +oc_sec_credusage_t +oc_cred_usage_from_string(const char *str, size_t str_len) +{ + oc_string_view_t view = oc_string_view(str, str_len); + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_CREDUSAGE_TRUSTCA_STR), view)) { + return OC_CREDUSAGE_TRUSTCA; + } + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_CREDUSAGE_IDENTITY_CERT_STR), + view)) { + return OC_CREDUSAGE_IDENTITY_CERT; + } + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_CREDUSAGE_ROLE_CERT_STR), + view)) { + return OC_CREDUSAGE_ROLE_CERT; + } + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_CREDUSAGE_MFG_TRUSTCA_STR), + view)) { + return OC_CREDUSAGE_MFG_TRUSTCA; + } + if (oc_string_view_is_equal(OC_STRING_VIEW(OC_CREDUSAGE_MFG_CERT_STR), + view)) { + return OC_CREDUSAGE_MFG_CERT; + } + return OC_CREDUSAGE_NULL; +} + +oc_sec_credusage_t +oc_cred_parse_credusage(const oc_string_t *credusage_string) +{ + return oc_cred_usage_from_string(oc_string(*credusage_string), + oc_string_len(*credusage_string)); +} + +typedef struct +{ + oc_sec_cred_filter_t filter; + void *filter_data; + char *buffer; + size_t buffer_size; + + bool ok; +} cred_serialize_iterate_data_t; + +static bool +cred_serialize_iterate(const oc_sec_cred_t *cred, void *data) +{ + cred_serialize_iterate_data_t *sid = (cred_serialize_iterate_data_t *)data; + if (cred->credtype != OC_CREDTYPE_CERT || + (sid->filter != NULL && !sid->filter(cred, sid->filter_data))) { + // skip to next cred + return true; + } + + // we can serialize only public data in PEM + if (cred->publicdata.encoding != OC_ENCODING_PEM) { + return true; + } + + const char *cred_pem = oc_string(cred->publicdata.data); + size_t cred_pem_len = oc_string_len(cred->publicdata.data); + assert(cred_pem_len != 0); + if (sid->buffer == NULL) { + sid->buffer_size += cred_pem_len; + return true; + } + + if (cred_pem_len > sid->buffer_size) { + OC_ERR("cannot serialize certificate: buffer too small"); + sid->ok = false; + return false; + } + + // write to buffer + memcpy(sid->buffer, cred_pem, cred_pem_len); + sid->buffer += cred_pem_len; + sid->buffer_size -= cred_pem_len; + return true; +} + +long +oc_cred_serialize(const oc_list_t creds, oc_sec_cred_filter_t filter, + void *filter_data, char *buffer, size_t buffer_size) +{ + assert(buffer != NULL || buffer_size == 0); + + cred_serialize_iterate_data_t sid = { + .filter = filter, + .filter_data = filter_data, + .buffer = buffer, + .buffer_size = buffer_size, + .ok = true, + }; + + oc_cred_iterate(creds, cred_serialize_iterate, &sid); + if (!sid.ok) { + return -1; + } + + if (buffer == NULL) { + return (long)(sid.buffer_size); + } + + long written = (long)(buffer_size - sid.buffer_size); + return written; +} + +#endif /* OC_PKI */ + +#endif /* OC_SECURITY */ diff --git a/security/oc_cred_util_internal.h b/security/oc_cred_util_internal.h new file mode 100644 index 000000000..f27859b27 --- /dev/null +++ b/security/oc_cred_util_internal.h @@ -0,0 +1,148 @@ +/**************************************************************************** + * + * Copyright (c) 2016-2019 Intel Corporation + * Copyright (c) 2023 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef OC_CRED_UTIL_INTERNAL_H +#define OC_CRED_UTIL_INTERNAL_H + +#include "api/oc_helpers_internal.h" +#include "oc_cred.h" +#include "security/oc_cred_internal.h" +#include "util/oc_list.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OC_ENCODING_BASE64_STR "oic.sec.encoding.base64" +#define OC_ENCODING_RAW_STR "oic.sec.encoding.raw" +#define OC_ENCODING_HANDLE_STR "oic.sec.encoding.handle" +#ifdef OC_PKI +#define OC_ENCODING_PEM_STR "oic.sec.encoding.pem" + +#define OC_CREDUSAGE_TRUSTCA_STR "oic.sec.cred.trustca" +#define OC_CREDUSAGE_IDENTITY_CERT_STR "oic.sec.cred.cert" +#define OC_CREDUSAGE_ROLE_CERT_STR "oic.sec.cred.rolecert" +#define OC_CREDUSAGE_MFG_TRUSTCA_STR "oic.sec.cred.mfgtrustca" +#define OC_CREDUSAGE_MFG_CERT_STR "oic.sec.cred.mfgcert" +#endif /* OC_PKI */ + +#define OC_CREDTYPE_PSK_STR "Symmetric pair-wise key" +#define OC_CREDTYPE_CERT_STR "Asymmetric signing key with certificate" + +/** Convert encoding to oc_string_view_t */ +oc_string_view_t oc_cred_encoding_to_string(oc_sec_encoding_t encoding); + +/** + * @brief Parse cred encoding from string + * + * @param str string (cannot be NULL) + * @param str_len length of \p str + * @return oc_sec_encoding_t parsed encoding + */ +oc_sec_encoding_t oc_cred_encoding_from_string(const char *str, size_t str_len) + OC_NONNULL(); + +/** Compare cred_data_t and oc_sec_encoded_data_t. */ +bool oc_cred_data_is_equal_to_encoded_data(oc_cred_data_t cd, + oc_sec_encoded_data_t sed); + +/** Check if the tag of the credential is equal to given value */ +bool oc_cred_has_tag(const oc_sec_cred_t *cred, oc_string_view_t tag) + OC_NONNULL(); + +/** Check if the input data is duplicate of the credential data */ +bool oc_cred_is_duplicate(const oc_sec_cred_t *cred, oc_sec_credtype_t credtype, + oc_uuid_t subject, oc_string_view_t tag, + oc_sec_encoded_data_t privatedata, + oc_sec_encoded_data_t publicdata, + oc_sec_credusage_t credusage) OC_NONNULL(); + +/** @brief Callback invoked for each cred of the device + * + * @param cred current credential (cannot be NULL) + * @param user_data user data passed to oc_cred_iterate + * @return true to continue iteration + * @return false to stop iteration + */ +typedef bool (*oc_cred_iterate_fn_t)(const oc_sec_cred_t *cred, void *user_data) + OC_NONNULL(1); + +/** + * @brief Iterate resources of given device + * + * @param creds credentials to iterate (must be oc_list_t of oc_sec_cred_t *) + * @param iterate iterating function (cannot be NULL) + * @param iterate_data user data passed from the caller + */ +void oc_cred_iterate(const oc_list_t creds, oc_cred_iterate_fn_t iterate, + void *iterate_data) OC_NONNULL(2); + +/** Set subject UUID based on the input string and credential usage */ +bool oc_sec_cred_set_subject(const char *subjectuuid, + oc_sec_credusage_t credusage, oc_uuid_t *subject) + OC_NONNULL(3); + +/** Set private data on the credential */ +bool oc_cred_set_privatedata(oc_sec_cred_t *cred, const uint8_t *data, + size_t data_size, oc_sec_encoding_t encoding) + OC_NONNULL(); + +#ifdef OC_PKI + +/** @brief Convert credusage to oc_string_view_t */ +oc_string_view_t oc_cred_credusage_to_string(oc_sec_credusage_t credusage); + +/** + * @brief Parse cred usage from string + * + * @param str string (cannot be NULL) + * @param str_len length of \p str + * @return oc_sec_credusage_t parsed usage + */ +oc_sec_credusage_t oc_cred_usage_from_string(const char *str, size_t str_len) + OC_NONNULL(); + +/** + * @brief Serialize matched certificates credential in PEM format to buffer + * + * @param creds credentials to iterate (must be oc_list_t of oc_sec_cred_t *) + * @param filter credential filtering function (if NULL all credentials will be + * used) + * @param filter_data user data passed to \p filter + * @param buffer buffer to store the output (if NULL the function will only + * calculate size) + * @param buffer_size size of the output buffer + * + * @return -1 on error + * @return >= 0 number of written bytes (excluding the null-terminator) + */ +long oc_cred_serialize(const oc_list_t creds, oc_sec_cred_filter_t filter, + void *filter_data, char *buffer, size_t buffer_size) + OC_NONNULL(1); + +#endif /* OC_PKI */ + +#ifdef __cplusplus +} +#endif + +#endif /* OC_CRED_INTERNAL_H */ diff --git a/security/oc_csr.c b/security/oc_csr.c index 743d75d3d..20adcffe1 100644 --- a/security/oc_csr.c +++ b/security/oc_csr.c @@ -29,6 +29,7 @@ #include "oc_uuid.h" #include "port/oc_log_internal.h" #include "security/oc_certs_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_csr_internal.h" #include "security/oc_entropy_internal.h" #include "security/oc_keypair_internal.h" diff --git a/security/oc_doxm_internal.h b/security/oc_doxm_internal.h index f29388d79..55c01e112 100644 --- a/security/oc_doxm_internal.h +++ b/security/oc_doxm_internal.h @@ -44,6 +44,12 @@ typedef enum oc_sec_doxmtype_t { OC_OXMTYPE_MFG_CERT = 2 } oc_sec_oxmtype_t; +#define OC_OXMTYPE_JW_STR "oic.sec.doxm.jw" +#define OC_OXMTYPE_RDP_STR "oic.sec.doxm.rdp" +#ifdef OC_PKI +#define OC_OXMTYPE_MFG_CERT_STR "oic.sec.doxm.mfgcert" +#endif /* OC_PKI */ + /// Device Owner Transfer Resource representation typedef struct { diff --git a/security/oc_obt.c b/security/oc_obt.c index d7e4d73e0..ad5902d7d 100644 --- a/security/oc_obt.c +++ b/security/oc_obt.c @@ -41,6 +41,7 @@ check oc_config.h and make sure OC_STORAGE is defined if OC_SECURITY is defined. #include "security/oc_acl_internal.h" #include "security/oc_certs_internal.h" #include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_csr_internal.h" #include "security/oc_doxm_internal.h" #include "security/oc_keypair_internal.h" @@ -442,7 +443,7 @@ free_otm_state(oc_otm_ctx_t *o, int status, oc_obt_otm_t otm) if (status == -1) { char suuid[OC_UUID_LEN]; oc_uuid_to_str(&o->device->uuid, suuid, OC_UUID_LEN); - oc_cred_remove_subject(suuid, 0); + oc_cred_remove_by_subject(suuid, 0); o->cb.cb(&o->device->uuid, status, o->cb.data); free_device(o->device); } else { @@ -909,7 +910,7 @@ free_hard_reset_ctx(oc_hard_reset_ctx_t *ctx, int status) /* Remove device's credential from OBT's credential store */ char subjectuuid[OC_UUID_LEN]; oc_uuid_to_str(&ctx->device->uuid, subjectuuid, OC_UUID_LEN); - oc_cred_remove_subject(subjectuuid, 0); + oc_cred_remove_by_subject(subjectuuid, 0); cb.cb(&ctx->device->uuid, 0, cb.data); } else { cb.cb(&ctx->device->uuid, -1, cb.data); diff --git a/security/oc_obt_otm_cert.c b/security/oc_obt_otm_cert.c index d9c210772..5dea45828 100644 --- a/security/oc_obt_otm_cert.c +++ b/security/oc_obt_otm_cert.c @@ -30,6 +30,7 @@ #include "oc_store.h" #include "security/oc_acl_internal.h" #include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_doxm_internal.h" #include "security/oc_obt_internal.h" #include "security/oc_pstat_internal.h" @@ -353,25 +354,20 @@ obt_cert_9(oc_client_response_t *data) char suuid[OC_UUID_LEN]; oc_uuid_to_str(&device->uuid, suuid, OC_UUID_LEN); -#define OXM_MFG_CERT "oic.sec.doxm.mfgcert" uint8_t key[16]; bool derived = oc_sec_derive_owner_psk( - ep, (const uint8_t *)OXM_MFG_CERT, OC_CHAR_ARRAY_LEN(OXM_MFG_CERT), - device->uuid.id, OC_ARRAY_SIZE(device->uuid.id), my_uuid->id, - OC_ARRAY_SIZE(my_uuid->id), key, OC_ARRAY_SIZE(key)); -#undef OXM_MFG_CERT + ep, (const uint8_t *)OC_OXMTYPE_MFG_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_MFG_CERT_STR), device->uuid.id, + OC_ARRAY_SIZE(device->uuid.id), my_uuid->id, OC_ARRAY_SIZE(my_uuid->id), + key, OC_ARRAY_SIZE(key)); if (!derived) { goto err_obt_cert_9; } oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), OC_ENCODING_RAW }; - oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = - oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - suuid, privatedata, publicdata, oc_string_view2(NULL), - oc_string_view2(NULL), oc_string_view2(NULL), NULL); - + oc_sec_add_new_psk_cred(0, suuid, privatedata, OC_STRING_VIEW_NULL); if (credid == -1) { goto err_obt_cert_9; } @@ -705,7 +701,7 @@ oc_obt_perform_cert_otm(const oc_uuid_t *uuid, oc_obt_device_status_cb_t cb, if (oc_obt_is_owned_device(uuid)) { char subjectuuid[OC_UUID_LEN]; oc_uuid_to_str(uuid, subjectuuid, OC_UUID_LEN); - oc_cred_remove_subject(subjectuuid, 0); + oc_cred_remove_by_subject(subjectuuid, 0); } oc_otm_ctx_t *o = oc_obt_alloc_otm_ctx(); diff --git a/security/oc_obt_otm_justworks.c b/security/oc_obt_otm_justworks.c index c14f48bca..270f99019 100644 --- a/security/oc_obt_otm_justworks.c +++ b/security/oc_obt_otm_justworks.c @@ -29,6 +29,7 @@ #include "oc_store.h" #include "security/oc_acl_internal.h" #include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_doxm_internal.h" #include "security/oc_obt_internal.h" #include "security/oc_pstat_internal.h" @@ -352,25 +353,20 @@ obt_jw_9(oc_client_response_t *data) char suuid[OC_UUID_LEN]; oc_uuid_to_str(&device->uuid, suuid, OC_UUID_LEN); -#define OXM_JUST_WORKS "oic.sec.doxm.jw" uint8_t key[16]; bool derived = oc_sec_derive_owner_psk( - ep, (const uint8_t *)OXM_JUST_WORKS, OC_CHAR_ARRAY_LEN(OXM_JUST_WORKS), - device->uuid.id, OC_ARRAY_SIZE(device->uuid.id), my_uuid->id, - OC_ARRAY_SIZE(my_uuid->id), key, OC_ARRAY_SIZE(key)); -#undef OXM_JUST_WORKS + ep, (const uint8_t *)OC_OXMTYPE_JW_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_JW_STR), device->uuid.id, + OC_ARRAY_SIZE(device->uuid.id), my_uuid->id, OC_ARRAY_SIZE(my_uuid->id), + key, OC_ARRAY_SIZE(key)); if (!derived) { goto err_obt_jw_9; } oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), OC_ENCODING_RAW }; - oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = - oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - suuid, privatedata, publicdata, oc_string_view2(NULL), - oc_string_view2(NULL), oc_string_view2(NULL), NULL); - + oc_sec_add_new_psk_cred(0, suuid, privatedata, OC_STRING_VIEW_NULL); if (credid == -1) { goto err_obt_jw_9; } @@ -704,7 +700,7 @@ oc_obt_perform_just_works_otm(const oc_uuid_t *uuid, if (oc_obt_is_owned_device(uuid)) { char subjectuuid[OC_UUID_LEN]; oc_uuid_to_str(uuid, subjectuuid, OC_UUID_LEN); - oc_cred_remove_subject(subjectuuid, 0); + oc_cred_remove_by_subject(subjectuuid, 0); } oc_otm_ctx_t *o = oc_obt_alloc_otm_ctx(); diff --git a/security/oc_obt_otm_randompin.c b/security/oc_obt_otm_randompin.c index 70f6f73a2..f5d191a49 100644 --- a/security/oc_obt_otm_randompin.c +++ b/security/oc_obt_otm_randompin.c @@ -29,6 +29,7 @@ #include "oc_store.h" #include "security/oc_acl_internal.h" #include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_doxm_internal.h" #include "security/oc_obt_internal.h" #include "security/oc_pstat_internal.h" @@ -353,25 +354,20 @@ obt_rdp_7(oc_client_response_t *data) char suuid[OC_UUID_LEN]; oc_uuid_to_str(&device->uuid, suuid, OC_UUID_LEN); -#define OXM_RDP "oic.sec.doxm.rdp" uint8_t key[16]; bool derived = oc_sec_derive_owner_psk( - ep, (const uint8_t *)OXM_RDP, OC_CHAR_ARRAY_LEN(OXM_RDP), device->uuid.id, + ep, (const uint8_t *)OC_OXMTYPE_RDP_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_RDP_STR), device->uuid.id, OC_ARRAY_SIZE(device->uuid.id), my_uuid->id, OC_ARRAY_SIZE(my_uuid->id), key, OC_ARRAY_SIZE(key)); -#undef OXM_RDP if (!derived) { goto err_obt_rdp_7; } oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), OC_ENCODING_RAW }; - oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = - oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - suuid, privatedata, publicdata, oc_string_view2(NULL), - oc_string_view2(NULL), oc_string_view2(NULL), NULL); - + oc_sec_add_new_psk_cred(0, suuid, privatedata, OC_STRING_VIEW_NULL); if (credid == -1) { goto err_obt_rdp_7; } @@ -541,7 +537,7 @@ obt_rdp_3(oc_client_response_t *data) */ char suuid[37]; oc_uuid_to_str(&device->uuid, suuid, OC_ARRAY_SIZE(suuid)); - oc_cred_remove_subject(suuid, 0); + oc_cred_remove_by_subject(suuid, 0); /* Store peer device's random uuid in local device object */ memcpy(device->uuid.id, dev_uuid.id, OC_ARRAY_SIZE(dev_uuid.id)); @@ -633,7 +629,7 @@ oc_obt_perform_random_pin_otm(const oc_uuid_t *uuid, const unsigned char *pin, if (oc_obt_is_owned_device(uuid)) { char subjectuuid[OC_UUID_LEN]; oc_uuid_to_str(uuid, subjectuuid, OC_UUID_LEN); - oc_cred_remove_subject(subjectuuid, 0); + oc_cred_remove_by_subject(subjectuuid, 0); } uint8_t key[16] = { 0 }; @@ -658,12 +654,8 @@ oc_obt_perform_random_pin_otm(const oc_uuid_t *uuid, const unsigned char *pin, oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), OC_ENCODING_RAW }; - oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; - int credid = oc_sec_add_new_cred( - 0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, subjectuuid, - privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), - oc_string_view2(NULL), NULL); - + int credid = + oc_sec_add_new_psk_cred(0, subjectuuid, privatedata, OC_STRING_VIEW_NULL); if (credid == -1) { oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP); return -1; diff --git a/security/oc_pki.c b/security/oc_pki.c index cf45d2bf2..31842645d 100644 --- a/security/oc_pki.c +++ b/security/oc_pki.c @@ -20,6 +20,7 @@ #if defined(OC_SECURITY) && defined(OC_PKI) +#include "api/oc_core_res_internal.h" #include "oc_pki.h" #include "oc_certs_internal.h" #include "oc_cred_internal.h" @@ -54,8 +55,6 @@ pki_add_intermediate_cert(size_t device, int credid, const unsigned char *cert, /* Parse the to-be-added intermediate cert */ size_t c_size = cert_size; - mbedtls_x509_crt int_ca; - mbedtls_x509_crt_init(&int_ca); if (!oc_certs_is_PEM(cert, cert_size)) { OC_ERR("provided cert is not in PEM format"); return -1; @@ -64,9 +63,12 @@ pki_add_intermediate_cert(size_t device, int credid, const unsigned char *cert, c_size += 1; } + mbedtls_x509_crt int_ca; + mbedtls_x509_crt_init(&int_ca); int ret = mbedtls_x509_crt_parse(&int_ca, cert, c_size); if (ret < 0) { OC_ERR("could not parse intermediate cert: %d", ret); + mbedtls_x509_crt_free(&int_ca); return -1; } OC_DBG("parsed intermediate CA cert"); @@ -82,6 +84,7 @@ pki_add_intermediate_cert(size_t device, int credid, const unsigned char *cert, OC_ERR("could not parse existing identity cert that chains to this " "intermediate cert: %d", ret); + mbedtls_x509_crt_free(&id_cert_chain); mbedtls_x509_crt_free(&int_ca); return -1; } @@ -137,16 +140,86 @@ pki_add_intermediate_cert(size_t device, int credid, const unsigned char *cert, return credid; } +static bool +pki_is_equal_certificate(const mbedtls_x509_crt *cert1, + const mbedtls_x509_crt *cert2) +{ + return cert1->raw.len == cert2->raw.len && + memcmp(cert1->raw.p, cert2->raw.p, cert2->raw.len) == 0; +} + +static int +pkg_match_certificate(const mbedtls_x509_crt *cert, + oc_sec_credusage_t cert_credusage, + const oc_sec_cred_t *cred) +{ + if (cred->credusage != cert_credusage) { + return -1; + } + + int credid = -1; + mbedtls_x509_crt cert2; + mbedtls_x509_crt_init(&cert2); + int ret = mbedtls_x509_crt_parse( + &cert2, oc_cast(cred->publicdata.data, unsigned char), + oc_string_len(cred->publicdata.data) + 1); + if (ret < 0) { + OC_ERR("could not parse stored certificate: %d", ret); + goto exit; + } + + if (cert_credusage == OC_CREDUSAGE_TRUSTCA || + cert_credusage == OC_CREDUSAGE_MFG_TRUSTCA) { + mbedtls_x509_crt *trustca = &cert2; + for (; trustca != NULL; trustca = trustca->next) { + if (pki_is_equal_certificate(cert, trustca)) { + OC_DBG("found trustca in cred with credid %d", cred->credid); + credid = cred->credid; + goto exit; + } + } + goto exit; + } + if ((cert_credusage == OC_CREDUSAGE_IDENTITY_CERT || + cert_credusage == OC_CREDUSAGE_MFG_CERT) && + pki_is_equal_certificate(cert, &cert2)) { + OC_DBG("found identity cert in cred with credid %d", cred->credid); + credid = cred->credid; + goto exit; + } + +exit: + mbedtls_x509_crt_free(&cert2); + return credid; +} + +static int +pki_find_certificate(size_t device, const mbedtls_x509_crt *cert, + oc_sec_credusage_t credusage) +{ + oc_sec_creds_t *creds = oc_sec_get_creds(device); + assert(creds != NULL); + oc_sec_cred_t *c = oc_list_head(creds->creds); + for (; c != NULL; c = c->next) { + int credid = pkg_match_certificate(cert, credusage, c); + if (credid != -1) { + return credid; + } + } + return -1; +} + static int pki_add_identity_cert(size_t device, const unsigned char *cert, size_t cert_size, const unsigned char *key, size_t key_size, oc_sec_credusage_t credusage) { + if (!oc_core_device_is_valid(device)) { + OC_ERR("invalid device index"); + return -1; + } OC_DBG("attempting to add an identity certificate chain"); - mbedtls_pk_context pkey; - mbedtls_pk_init(&pkey); - if (!oc_certs_is_PEM(cert, cert_size)) { OC_ERR("provided cert is not in PEM format"); return -1; @@ -165,11 +238,14 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, } /* Parse identity cert's private key */ + mbedtls_pk_context pkey; + mbedtls_pk_init(&pkey); int ret = oc_mbedtls_pk_parse_key(device, &pkey, key, k_size, NULL, 0, mbedtls_ctr_drbg_random, oc_tls_ctr_drbg_context()); if (ret != 0) { OC_ERR("could not parse identity cert's private key %d", ret); + mbedtls_pk_free(&pkey); return -1; } OC_DBG("parsed the provided identity cert's private key"); @@ -194,6 +270,7 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, ret = mbedtls_x509_crt_parse(&cert1, cert, c_size); if (ret < 0) { OC_ERR("could not parse the provided identity cert"); + mbedtls_x509_crt_free(&cert1); return -1; } OC_DBG("parsed the provided identity cert"); @@ -206,56 +283,31 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, subjectuuid[0] = '*'; subjectuuid[1] = '\0'; } - oc_sec_creds_t *creds = oc_sec_get_creds(device); - oc_sec_cred_t *c = oc_list_head(creds->creds); - for (; c != NULL; c = c->next) { - /* Iterate over all identity certs provisioned to this logical - * device. - */ - if (c->credusage != credusage) { - continue; - } - mbedtls_x509_crt cert2; - mbedtls_x509_crt_init(&cert2); - - ret = - mbedtls_x509_crt_parse(&cert2, oc_cast(c->publicdata.data, unsigned char), - oc_string_len(c->publicdata.data) + 1); - if (ret < 0) { - mbedtls_x509_crt_free(&cert2); - continue; - } - - if (cert1.raw.len == cert2.raw.len && - memcmp(cert1.raw.p, cert2.raw.p, cert2.raw.len) == 0) { - mbedtls_x509_crt_free(&cert1); - mbedtls_x509_crt_free(&cert2); - OC_DBG("found identity cert in cred with credid %d", c->credid); - return c->credid; - } - mbedtls_x509_crt_free(&cert2); + int credid = pki_find_certificate(device, &cert1, credusage); + mbedtls_x509_crt_free(&cert1); + if (credid != -1) { + OC_DBG("found identity cert in cred with credid %d", credid); + return credid; } OC_DBG("adding a new identity cert chain to /oic/sec/cred"); - mbedtls_x509_crt_free(&cert1); - - oc_sec_encoded_data_t privatedata = { privkbuf + (200 - private_key_size), + oc_sec_encoded_data_t privatedata = { privkbuf + + (sizeof(privkbuf) - private_key_size), private_key_size, OC_ENCODING_RAW }; oc_sec_encoded_data_t publicdata = { cert, c_size - 1, OC_ENCODING_PEM }; - int credid = oc_sec_add_new_cred( - device, false, NULL, -1, OC_CREDTYPE_CERT, credusage, subjectuuid, - privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), - oc_string_view2(NULL), NULL); - - if (credid != -1) { - OC_DBG("added new identity cert(credid=%d) chain to /oic/sec/cred", credid); - oc_sec_dump_cred(device); - } else { + ret = oc_sec_add_new_cred(device, false, NULL, -1, OC_CREDTYPE_CERT, + credusage, subjectuuid, privatedata, publicdata, + OC_STRING_VIEW_NULL, OC_STRING_VIEW_NULL, + oc_string_view2(NULL), NULL); + if (ret == -1) { OC_ERR("could not add identity cert chain to /oic/sec/cred"); + return -1; } - return credid; + OC_DBG("added new identity cert(credid=%d) chain to /oic/sec/cred", ret); + oc_sec_dump_cred(device); + return ret; } int @@ -286,7 +338,12 @@ static int pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, oc_sec_credusage_t credusage) { + if (!oc_core_device_is_valid(device)) { + OC_ERR("invalid device index"); + return -1; + } OC_DBG("attempting to add a trust anchor"); + /* Parse root cert */ if (!oc_certs_is_PEM(cert, cert_size)) { OC_ERR("provided cert is not in PEM format"); @@ -301,43 +358,17 @@ pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, int ret = mbedtls_x509_crt_parse(&cert1, cert, c_size); if (ret < 0) { OC_ERR("could not parse the provided trust anchor: %d", ret); + mbedtls_x509_crt_free(&cert1); return -1; } OC_DBG("parsed the provided trust anchor"); /* Pass through all known trust anchors looking for a match */ - oc_sec_creds_t *creds = oc_sec_get_creds(device); - oc_sec_cred_t *c = oc_list_head(creds->creds); - for (; c != NULL; c = c->next) { - if (c->credusage != credusage) { - continue; - } - mbedtls_x509_crt cert2; - mbedtls_x509_crt_init(&cert2); - ret = - mbedtls_x509_crt_parse(&cert2, oc_cast(c->publicdata.data, unsigned char), - oc_string_len(c->publicdata.data) + 1); - if (ret < 0) { - OC_ERR("could not parse stored certificate: %d", ret); - mbedtls_x509_crt_free(&cert2); - continue; - } - - mbedtls_x509_crt *trustca = &cert2; - for (; trustca != NULL; trustca = trustca->next) { - if (trustca->raw.len == cert1.raw.len && - memcmp(trustca->raw.p, cert1.raw.p, cert1.raw.len) == 0) { - break; - } - } - - mbedtls_x509_crt_free(&cert2); - - if (trustca) { - mbedtls_x509_crt_free(&cert1); - OC_DBG("found trust anchor in cred with credid %d", c->credid); - return c->credid; - } + int credid = pki_find_certificate(device, &cert1, credusage); + mbedtls_x509_crt_free(&cert1); + if (credid != -1) { + OC_DBG("found trust anchor in cred with credid %d", credid); + return credid; } OC_DBG("adding a new trust anchor entry to /oic/sec/cred"); @@ -346,16 +377,15 @@ pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, oc_sec_encoded_data_t publicdata = { cert, c_size - 1, OC_ENCODING_PEM }; ret = oc_sec_add_new_cred(device, false, NULL, -1, OC_CREDTYPE_CERT, credusage, - "*", privatedata, publicdata, oc_string_view2(NULL), - oc_string_view2(NULL), oc_string_view2(NULL), NULL); - if (ret != -1) { - OC_DBG("added new trust anchor entry to /oic/sec/cred"); - oc_sec_dump_cred(device); - } else { + "*", privatedata, publicdata, OC_STRING_VIEW_NULL, + OC_STRING_VIEW_NULL, OC_STRING_VIEW_NULL, NULL); + if (ret == -1) { OC_ERR("could not add trust anchor entry to /oic/sec/cred"); + return -1; } - mbedtls_x509_crt_free(&cert1); + OC_DBG("added new trust anchor entry to /oic/sec/cred"); + oc_sec_dump_cred(device); return ret; } diff --git a/security/oc_roles.c b/security/oc_roles.c index 534eab58a..c5809d303 100644 --- a/security/oc_roles.c +++ b/security/oc_roles.c @@ -23,8 +23,9 @@ #include "api/oc_core_res_internal.h" #include "api/oc_helpers_internal.h" #include "port/oc_log_internal.h" -#include "oc_roles_internal.h" -#include "oc_tls_internal.h" +#include "security/oc_cred_util_internal.h" +#include "security/oc_roles_internal.h" +#include "security/oc_tls_internal.h" #include diff --git a/security/oc_svr.c b/security/oc_svr.c index 912ba8fdf..ed35991c8 100644 --- a/security/oc_svr.c +++ b/security/oc_svr.c @@ -76,7 +76,7 @@ oc_sec_svr_free(void) oc_sec_sdi_free(); oc_sec_sp_free(); oc_sec_ael_free(); - oc_sec_cred_free(); + oc_sec_cred_deinit(); oc_sec_acl_free(); oc_sec_pstat_free(); oc_sec_doxm_free(); diff --git a/security/oc_tls.c b/security/oc_tls.c index 303270bb1..b556632d2 100644 --- a/security/oc_tls.c +++ b/security/oc_tls.c @@ -846,7 +846,9 @@ get_psk_cb(void *data, mbedtls_ssl_context *ssl, const unsigned char *identity, } const oc_sec_doxm_t *doxm = oc_sec_get_doxm(peer->endpoint.device); if (ps->s == OC_DOS_RFOTM && doxm->oxmsel == OC_OXMTYPE_RDP) { - if (identity_len != 16 || memcmp(identity, "oic.sec.doxm.rdp", 16) != 0) { + if (identity_len != OC_CHAR_ARRAY_LEN(OC_OXMTYPE_RDP_STR) || + memcmp(identity, OC_OXMTYPE_RDP_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_RDP_STR)) != 0) { OC_ERR("oc_tls: OBT identity incorrectly set for PIN OTM"); return -1; } @@ -953,6 +955,7 @@ is_known_identity_cert(const oc_sec_cred_t *cred) cert, (const unsigned char *)oc_string(cred->publicdata.data), cert_len); if (ret < 0) { OC_ERR("could not parse identity cert from cred"); + mbedtls_x509_crt_free(cert); return true; } @@ -1918,8 +1921,8 @@ oc_tls_populate_ssl_config(mbedtls_ssl_config *conf, size_t device, int role, if (role == MBEDTLS_SSL_IS_CLIENT && use_pin_obt_psk_identity) { use_pin_obt_psk_identity = false; if (mbedtls_ssl_conf_psk(conf, device_id->id, 1, - (const unsigned char *)"oic.sec.doxm.rdp", - 16) != 0) { + (const unsigned char *)OC_OXMTYPE_RDP_STR, + OC_CHAR_ARRAY_LEN(OC_OXMTYPE_RDP_STR)) != 0) { return -1; } } else diff --git a/security/unittest/certsgeneratetest.cpp b/security/unittest/certsgeneratetest.cpp index 1a814e930..d28df34de 100644 --- a/security/unittest/certsgeneratetest.cpp +++ b/security/unittest/certsgeneratetest.cpp @@ -25,6 +25,7 @@ #include "security/oc_certs_generate_internal.h" #include "security/oc_certs_internal.h" #include "tests/gtest/KeyPair.h" +#include "tests/gtest/PKI.h" #include "tests/gtest/Role.h" #include "tests/gtest/Utility.h" @@ -278,28 +279,6 @@ defaultCertificateGenerate(const oc::keypair_t &kp, bool isCA = false) return generate; } -static std::vector -getCertificate(const oc_certs_generate_t &generate) -{ - std::vector cert_buf{}; - cert_buf.resize(4096, '\0'); - int err = oc_certs_generate(&generate, cert_buf.data(), cert_buf.size()); - EXPECT_EQ(0, err); - if (err != 0) { - return {}; - } - - auto it = std::find(cert_buf.begin(), cert_buf.end(), - static_cast('\0')); - size_t data_len = - std::distance(cert_buf.begin(), it) + 1; // size with NULL terminator - if (cert_buf.end() == it || !oc_certs_is_PEM(&cert_buf[0], data_len)) { - return {}; - } - cert_buf.resize(data_len); - return cert_buf; -} - TEST_F(TestParseCerts, ParseSerialNumber_Fail) { // invalid PEM @@ -311,7 +290,7 @@ TEST_F(TestParseCerts, ParseSerialNumber_Fail) oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; // longer than the buffer size generate.serial_number_size = 20; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); EXPECT_GT(0, oc_certs_parse_serial_number(pem.data(), pem.size(), &buffer[0], buffer.size())); @@ -321,7 +300,7 @@ TEST_F(TestParseCerts, ParseSerialNumber) { oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); std::array buffer{}; // certificate without serial number @@ -330,7 +309,7 @@ TEST_F(TestParseCerts, ParseSerialNumber) generate = defaultCertificateGenerate(kp); generate.serial_number_size = 10; - pem = getCertificate(generate); + pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); EXPECT_LT(0, oc_certs_parse_serial_number(pem.data(), pem.size(), &buffer[0], buffer.size())); @@ -344,7 +323,7 @@ TEST_F(TestParseCerts, ParsePrivateKey_Fail) oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); std::array too_small{}; EXPECT_GT(0, oc_certs_parse_private_key(0, pem.data(), pem.size(), @@ -355,7 +334,7 @@ TEST_F(TestParseCerts, ParsePrivateKey) { oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); std::array private_key{}; int ret = oc_certs_parse_private_key(0, pem.data(), pem.size(), @@ -371,7 +350,7 @@ TEST_F(TestParseCerts, ParsePublicKey_Fail) oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); std::array too_small{}; EXPECT_GT(0, oc_certs_parse_public_key(pem.data(), pem.size(), &too_small[0], @@ -382,7 +361,7 @@ TEST_F(TestParseCerts, ParsePublicKey) { oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); std::array public_key{}; int ret = oc_certs_parse_public_key(pem.data(), pem.size(), &public_key[0], @@ -414,7 +393,7 @@ TEST_F(TestParseCerts, ParsePublicKeyToOCString) oc_string_t public_key{}; oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); int ret = oc_certs_parse_public_key_to_oc_string(pem.data(), pem.size(), &public_key); @@ -433,7 +412,7 @@ TEST_F(TestParseCerts, ParseFirstRole_Fail) oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; oc_certs_generate_t generate{ defaultCertificateGenerate(kp) }; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); EXPECT_FALSE( oc_certs_parse_first_role(pem.data(), pem.size(), &role, &authority)); @@ -456,7 +435,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailInvalidSubjectRole) ASSERT_EQ(0, mbedtls_x509_string_to_names( &names.general_name.name.directory_name, invalidRole.c_str())); generate.modify_fn_data = &names; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); mbedtls_asn1_free_named_data_list(&names.general_name.name.directory_name); #else /* MBEDTLS_VERSION_NUMBER > 0x03010000 */ generate.modify_fn = [](mbedtls_x509write_cert *crt, const void *data) { @@ -470,7 +449,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailInvalidSubjectRole) ASSERT_EQ(0, mbedtls_x509_string_to_names(&namedData, invalidRole.c_str())); names.node.san.directory_name = *namedData; generate.modify_fn_data = &names; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); mbedtls_asn1_free_named_data_list(&namedData); #endif /* MBEDTLS_VERSION_NUMBER <= 0x03010000 */ ASSERT_FALSE(pem.empty()); @@ -498,7 +477,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailMissingSubjectRole) 0, mbedtls_x509_string_to_names(&names.general_name.name.directory_name, invalidSubject.c_str())); generate.modify_fn_data = &names; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); mbedtls_asn1_free_named_data_list(&names.general_name.name.directory_name); #else /* MBEDTLS_VERSION_NUMBER > 0x03010000 */ generate.modify_fn = [](mbedtls_x509write_cert *crt, const void *data) { @@ -513,7 +492,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailMissingSubjectRole) mbedtls_x509_string_to_names(&namedData, invalidSubject.c_str())); names.node.san.directory_name = *namedData; generate.modify_fn_data = &names; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); mbedtls_asn1_free_named_data_list(&namedData); #endif /* MBEDTLS_VERSION_NUMBER <= 0x03010000 */ ASSERT_FALSE(pem.empty()); @@ -539,7 +518,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailInvalidSubjectType) names.general_name.name.dns_name.p = invalidSubject.data(); names.general_name.name.dns_name.len = invalidSubject.size(); generate.modify_fn_data = &names; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); #else /* MBEDTLS_VERSION_NUMBER > 0x03010000 */ generate.modify_fn = [](mbedtls_x509write_cert *crt, const void *data) { const auto *alt_names = static_cast(data); @@ -551,7 +530,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailInvalidSubjectType) names.node.san.unstructured_name.p = invalidSubject.data(); names.node.san.unstructured_name.len = invalidSubject.size(); generate.modify_fn_data = &names; - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); #endif /* MBEDTLS_VERSION_NUMBER <= 0x03010000 */ ASSERT_FALSE(pem.empty()); oc_string_t role{}; @@ -568,7 +547,7 @@ TEST_F(TestParseCerts, ParseFirstRole_FailInvalidSubjectAuthority) oc::Roles roles{}; roles.Add("user1"); generate.roles = roles.Head(); - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); oc_string_t role{}; oc_string_t authority{}; @@ -584,7 +563,7 @@ TEST_F(TestParseCerts, ParseFirstRole) roles.Add("user1", "admin1"); roles.Add("user2", "admin2"); generate.roles = roles.Head(); - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); oc_string_t role{}; @@ -605,7 +584,7 @@ TEST_F(TestParseCerts, ParseFirstRoleGetAuthorityFromIssuer) oc::Roles roles{}; roles.Add("user1"); generate.roles = roles.Head(); - auto pem = getCertificate(generate); + auto pem = oc::pki::GenerateCertificate(generate); ASSERT_FALSE(pem.empty()); oc_string_t role{}; diff --git a/security/unittest/credtest.cpp b/security/unittest/credtest.cpp index b8f2d0496..3d9ad4bcf 100644 --- a/security/unittest/credtest.cpp +++ b/security/unittest/credtest.cpp @@ -1,6 +1,6 @@ -/****************************************************************** +/**************************************************************************** * - * Copyright 2022 Daniel Adam, All Rights Reserved. + * Copyright (c) 2023 plgd.dev s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"), * you may not use this file except in compliance with the License. @@ -8,109 +8,401 @@ * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. * - ******************************************************************/ + ****************************************************************************/ #ifdef OC_SECURITY -#include "oc_cred.h" -#include "oc_helpers.h" +#include "api/oc_core_res_internal.h" +#include "api/oc_ri_internal.h" +#include "api/oc_runtime_internal.h" +#include "oc_base64.h" +#include "oc_uuid.h" +#include "port/oc_connectivity.h" +#include "port/oc_network_event_handler_internal.h" +#include "security/oc_certs_generate_internal.h" +#include "security/oc_certs_internal.h" #include "security/oc_cred_internal.h" -#include "util/oc_macros_internal.h" +#include "security/oc_cred_util_internal.h" +#include "security/oc_obt_internal.h" +#include "security/oc_security_internal.h" +#include "security/oc_svr_internal.h" +#include "security/oc_tls_internal.h" +#include "tests/gtest/Device.h" +#include "tests/gtest/KeyPair.h" +#include "tests/gtest/PKI.h" +#ifdef OC_HAS_FEATURE_PUSH +#include "api/oc_push_internal.h" +#endif /* OC_HAS_FEATURE_PUSH */ + +#include +#include #include +#include +#include +#include + +static constexpr size_t kDeviceID{ 0 }; + +class TestCreds : public testing::Test { +public: + static void SetUpTestCase() + { + oc_network_event_handler_mutex_init(); + oc_runtime_init(); + oc_ri_init(); + oc_core_init(); + ASSERT_EQ(0, oc_add_device(oc::DefaultDevice.uri.c_str(), + oc::DefaultDevice.rt.c_str(), + oc::DefaultDevice.name.c_str(), + oc::DefaultDevice.spec_version.c_str(), + oc::DefaultDevice.data_model_version.c_str(), + nullptr, nullptr)); + oc_sec_svr_create(); + + oc_mbedtls_init(); + } + + static void TearDownTestCase() + { + oc_sec_svr_free(); +#ifdef OC_HAS_FEATURE_PUSH + oc_push_free(); +#endif /* OC_HAS_FEATURE_PUSH */ + oc_connectivity_shutdown(0); + oc_core_shutdown(); + oc_ri_shutdown(); + oc_runtime_shutdown(); + oc_network_event_handler_mutex_destroy(); + } + + void TearDown() override + { + oc_sec_cred_clear(kDeviceID, nullptr, nullptr); + } -class TestCred : public testing::Test {}; + static size_t countCreds(size_t device) + { + const oc_sec_creds_t *device_creds = oc_sec_get_creds(device); + if (device_creds == nullptr) { + return 0; + } -TEST_F(TestCred, ParseEncoding) + size_t count = 0; + oc_cred_iterate( + device_creds->creds, + [](const oc_sec_cred_t *, void *data) { + ++(*static_cast(data)); + return true; + }, + &count); + return count; + } + +#if defined(OC_PKI) && (defined(OC_DYNAMIC_ALLOCATION) || defined(OC_TEST)) + static int addRootCertificate(size_t device, const oc::keypair_t &kp, + bool isMfg = false) + { + auto pem = oc::pki::GenerateRootCertificate(kp); + return isMfg ? oc_pki_add_mfg_trust_anchor(device, pem.data(), pem.size()) + : oc_pki_add_trust_anchor(device, pem.data(), pem.size()); + } + + static int addIdentityCertificate(size_t device, const oc::keypair_t &kp, + const oc::keypair_t &issuer_kp, + bool isMfg = false) + { + auto pem = oc::pki::GeneratIdentityCertificate(kp, issuer_kp); + if (pem.empty()) { + return -1; + } + oc::pki::KeyParser parser{}; + auto keyPem = + parser.GetPrivateKey(kp.private_key.data(), kp.private_key_size); + if (keyPem.empty()) { + return -1; + } + + return isMfg ? oc_pki_add_mfg_cert(device, pem.data(), pem.size(), + keyPem.data(), keyPem.size()) + : oc_pki_add_identity_cert(device, pem.data(), pem.size(), + keyPem.data(), keyPem.size()); + } +#endif /* OC_PKI && (OC_DYNAMIC_ALLOCATION || OC_TEST) */ +}; + +TEST_F(TestCreds, GetCreds) { - oc_string_t enc{}; - oc_set_string(&enc, "", 0); - EXPECT_EQ(OC_ENCODING_UNSUPPORTED, oc_cred_parse_encoding(&enc)); + // invalid deviceID + EXPECT_EQ(nullptr, oc_sec_get_creds(42)); - oc_set_string(&enc, OC_ENCODING_BASE64_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_BASE64_STR)); - EXPECT_EQ(OC_ENCODING_BASE64, oc_cred_parse_encoding(&enc)); + // valid deviceID + EXPECT_NE(nullptr, oc_sec_get_creds(kDeviceID)); +} - oc_set_string(&enc, OC_ENCODING_RAW_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR)); - EXPECT_EQ(OC_ENCODING_RAW, oc_cred_parse_encoding(&enc)); +TEST_F(TestCreds, CreateAndRemovePSK) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); - oc_set_string(&enc, OC_ENCODING_HANDLE_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_HANDLE_STR)); - EXPECT_EQ(OC_ENCODING_HANDLE, oc_cred_parse_encoding(&enc)); + std::vector pin{ '1', '2', '3' }; + // 16 = SYMMETRIC_KEY_128BIT_LEN + std::array key{}; + ASSERT_EQ(0, oc_tls_pbkdf2(pin.data(), pin.size(), &uuid, 100, + MBEDTLS_MD_SHA256, &key[0], key.size())); -#ifdef OC_PKI - oc_set_string(&enc, OC_ENCODING_PEM_STR, - OC_CHAR_ARRAY_LEN(OC_ENCODING_PEM_STR)); - EXPECT_EQ(OC_ENCODING_PEM, oc_cred_parse_encoding(&enc)); -#endif /* OC_PKI */ + std::array uuid_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size())); + oc_sec_encoded_data_t privatedata = { key.data(), key.size(), + OC_ENCODING_RAW }; + int credid = oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL); + ASSERT_NE(-1, credid); + EXPECT_EQ(1, countCreds(kDeviceID)); + + EXPECT_NE(nullptr, oc_sec_get_cred_by_credid(credid, kDeviceID)); + + auto *cred = oc_sec_cred_remove_from_device_by_credid(credid, kDeviceID); + ASSERT_NE(nullptr, cred); + oc_sec_cred_free(cred); - oc_free_string(&enc); + EXPECT_EQ(nullptr, oc_sec_get_cred_by_credid(credid, kDeviceID)); } -TEST_F(TestCred, ReadEncoding) +TEST_F(TestCreds, CreatePSK_FailInvalidRawKey) { - EXPECT_STREQ(OC_ENCODING_BASE64_STR, - oc_cred_read_encoding(OC_ENCODING_BASE64)); - EXPECT_STREQ(OC_ENCODING_RAW_STR, oc_cred_read_encoding(OC_ENCODING_RAW)); - EXPECT_STREQ(OC_ENCODING_HANDLE_STR, - oc_cred_read_encoding(OC_ENCODING_HANDLE)); -#ifdef OC_PKI - EXPECT_STREQ(OC_ENCODING_PEM_STR, oc_cred_read_encoding(OC_ENCODING_PEM)); -#endif /* OC_PKI */ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + std::array uuid_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size())); + std::array key{ 'e', 'l', 'i', 't', 'e' }; + oc_sec_encoded_data_t privatedata = { key.data(), key.size(), + OC_ENCODING_RAW }; + EXPECT_EQ(-1, oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL)); +} + +TEST_F(TestCreds, CreateAndRemovePSKWithBase64Key) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + std::array uuid_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size())); + + std::vector pin{ '1', '2', '3' }; + // 32 = SYMMETRIC_KEY_256BIT_LEN + std::array key{}; + ASSERT_EQ(0, oc_tls_pbkdf2(pin.data(), pin.size(), &uuid, 100, + MBEDTLS_MD_SHA256, &key[0], key.size())); + + std::vector keyB64; + keyB64.resize(64); + int len = oc_base64_encode(key.data(), key.size(), &keyB64[0], keyB64.size()); + ASSERT_NE(-1, len); + keyB64.resize(len); + + oc_sec_encoded_data_t privatedata = { keyB64.data(), keyB64.size(), + OC_ENCODING_BASE64 }; + int credid = oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL); + ASSERT_NE(-1, credid); + EXPECT_EQ(1, countCreds(kDeviceID)); + + ASSERT_TRUE(oc_sec_remove_cred_by_credid(credid, kDeviceID)); +} + +TEST_F(TestCreds, CreatePSK_FailInvalidBase64Key) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + std::array uuid_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size())); + + std::vector notB64{ '1', '2', '3', '4', '5' }; + oc_sec_encoded_data_t privatedata = { notB64.data(), notB64.size(), + OC_ENCODING_BASE64 }; + EXPECT_EQ(-1, oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL)); + + std::vector tooLong(128, 'x'); + std::vector keyB64; + keyB64.resize(tooLong.size() * 2); + int len = + oc_base64_encode(tooLong.data(), tooLong.size(), &keyB64[0], keyB64.size()); + ASSERT_NE(-1, len); + keyB64.resize(len); + privatedata = { keyB64.data(), keyB64.size(), OC_ENCODING_BASE64 }; + EXPECT_EQ(-1, oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL)); + + // key length is not valid according to cred_check_symmetric_key_length + std::vector notSymmetric(5, 'x'); + keyB64.resize(notSymmetric.size() * 2); + len = oc_base64_encode(notSymmetric.data(), notSymmetric.size(), &keyB64[0], + keyB64.size()); + keyB64.resize(len); + privatedata = { keyB64.data(), keyB64.size(), OC_ENCODING_BASE64 }; + EXPECT_EQ(-1, oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL)); +} + +TEST_F(TestCreds, RemoveBySubjectID) +{ + oc_uuid_t uuid1{}; + oc_gen_uuid(&uuid1); + std::array uuid1_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid1, &uuid1_str[0], uuid1_str.size())); + + oc_uuid_t uuid2{}; + do { + oc_gen_uuid(&uuid2); + } while (oc_uuid_is_equal(uuid1, uuid2)); + std::array uuid2_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid2, &uuid2_str[0], uuid2_str.size())); + + oc_uuid_t uuid3{ '*' }; + std::array uuid3_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid3, &uuid3_str[0], uuid3_str.size())); + + std::vector pin{ '1', '2', '3' }; + auto add_psk_cred = [&pin](const oc_uuid_t &uuid) { + std::array key{}; + if (oc_tls_pbkdf2(pin.data(), pin.size(), &uuid, 100, MBEDTLS_MD_SHA256, + &key[0], key.size()) != 0) { + return -1; + } + std::array uuid_str{}; + if (oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size()) == -1) { + return -1; + } + oc_sec_encoded_data_t privatedata = { key.data(), key.size(), + OC_ENCODING_RAW }; + return oc_sec_add_new_psk_cred(kDeviceID, uuid_str.data(), privatedata, + OC_STRING_VIEW_NULL); + }; + int credid1 = add_psk_cred(uuid1); + ASSERT_NE(-1, credid1); + int credid2 = add_psk_cred(uuid2); + ASSERT_NE(-1, credid2); + + EXPECT_EQ(2, countCreds(kDeviceID)); + + EXPECT_EQ(nullptr, oc_cred_find_by_subject(uuid3_str.data(), kDeviceID)); + EXPECT_FALSE(oc_cred_remove_by_subject(uuid3_str.data(), kDeviceID)); + + EXPECT_NE(nullptr, oc_cred_find_by_subject(uuid2_str.data(), kDeviceID)); + EXPECT_TRUE(oc_sec_remove_cred_by_credid(credid2, kDeviceID)); + EXPECT_EQ(nullptr, oc_cred_find_by_subject(uuid2_str.data(), kDeviceID)); + EXPECT_FALSE(oc_sec_remove_cred_by_credid(credid2, kDeviceID)); + EXPECT_EQ(1, countCreds(kDeviceID)); + + EXPECT_NE(nullptr, oc_cred_find_by_subject(uuid1_str.data(), kDeviceID)); + EXPECT_TRUE(oc_cred_remove_by_subject(uuid1_str.data(), kDeviceID)); + EXPECT_EQ(nullptr, oc_cred_find_by_subject(uuid1_str.data(), kDeviceID)); + EXPECT_EQ(0, countCreds(kDeviceID)); } #ifdef OC_PKI -TEST_F(TestCred, ParseCredUsage) +TEST_F(TestCreds, Serialize) { - oc_string_t usage{}; - oc_set_string(&usage, "", 0); - EXPECT_EQ(OC_CREDUSAGE_NULL, oc_cred_parse_credusage(&usage)); + // root ca certificate + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int rootCredID = addRootCertificate(kDeviceID, rootKey); + ASSERT_LT(0, rootCredID); + +#ifdef OC_DYNAMIC_ALLOCATION + // identity certificate + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int identCredID = addIdentityCertificate(kDeviceID, identKey, rootKey); + ASSERT_LT(0, identCredID); +#endif /* OC_DYNAMIC_ALLOCATION */ - oc_set_string(&usage, OC_CREDUSAGE_TRUSTCA_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_TRUSTCA_STR)); - EXPECT_EQ(OC_CREDUSAGE_TRUSTCA, oc_cred_parse_credusage(&usage)); + oc_sec_creds_t *device_creds = oc_sec_get_creds(kDeviceID); + ASSERT_NE(nullptr, device_creds); - oc_set_string(&usage, OC_CREDUSAGE_IDENTITY_CERT_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_IDENTITY_CERT_STR)); - EXPECT_EQ(OC_CREDUSAGE_IDENTITY_CERT, oc_cred_parse_credusage(&usage)); + auto isCACertificate = [](const oc_sec_cred_t *cred, void *) { + return cred->credusage == OC_CREDUSAGE_TRUSTCA || + cred->credusage == OC_CREDUSAGE_MFG_TRUSTCA; + }; - oc_set_string(&usage, OC_CREDUSAGE_ROLE_CERT_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_ROLE_CERT_STR)); - EXPECT_EQ(OC_CREDUSAGE_ROLE_CERT, oc_cred_parse_credusage(&usage)); + auto caPEMSize = oc_cred_serialize(device_creds->creds, isCACertificate, + nullptr, nullptr, 0); + ASSERT_LT(0, caPEMSize); + std::vector caPEM{}; + caPEM.resize(caPEMSize); + ASSERT_EQ(caPEMSize, oc_cred_serialize(device_creds->creds, isCACertificate, + nullptr, &caPEM[0], caPEM.size())); + caPEM.resize(caPEMSize + 1); - oc_set_string(&usage, OC_CREDUSAGE_MFG_TRUSTCA_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_TRUSTCA_STR)); - EXPECT_EQ(OC_CREDUSAGE_MFG_TRUSTCA, oc_cred_parse_credusage(&usage)); +#ifdef OC_DYNAMIC_ALLOCATION + auto isIdentityCertificate = [](const oc_sec_cred_t *cred, void *) { + return cred->credusage == OC_CREDUSAGE_IDENTITY_CERT || + cred->credusage == OC_CREDUSAGE_MFG_CERT; + }; - oc_set_string(&usage, OC_CREDUSAGE_MFG_CERT_STR, - OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_CERT_STR)); - EXPECT_EQ(OC_CREDUSAGE_MFG_CERT, oc_cred_parse_credusage(&usage)); + auto identPEMSize = oc_cred_serialize( + device_creds->creds, isIdentityCertificate, nullptr, nullptr, 0); + ASSERT_LT(0, identPEMSize); + std::vector identPEM{}; + identPEM.resize(identPEMSize + 1); + ASSERT_EQ(identPEMSize, + oc_cred_serialize(device_creds->creds, isIdentityCertificate, + nullptr, &identPEM[0], identPEM.size())); +#endif /* OC_DYNAMIC_ALLOCATION */ - oc_free_string(&usage); + // no cred matches filter + EXPECT_EQ(0, oc_cred_serialize( + device_creds->creds, + [](const oc_sec_cred_t *, void *) { return false; }, nullptr, + nullptr, 0)); + + // match all + auto pemSize = + oc_cred_serialize(device_creds->creds, nullptr, nullptr, nullptr, 0); + ASSERT_LT(0, pemSize); + std::vector pem{}; + pem.resize(pemSize + 1); + ASSERT_EQ(pemSize, oc_cred_serialize(device_creds->creds, nullptr, nullptr, + &pem[0], pem.size())); + + std::vector expPem{}; + expPem.insert(expPem.end(), caPEM.begin(), caPEM.end()); +#ifdef OC_DYNAMIC_ALLOCATION + expPem.insert(expPem.end() - 1, identPEM.begin(), identPEM.end()); +#endif /* OC_DYNAMIC_ALLOCATION */ + EXPECT_STREQ(expPem.data(), pem.data()); } -TEST_F(TestCred, ReadCredUsage) +TEST_F(TestCreds, Serialize_Fail) { - EXPECT_STREQ(OC_CREDUSAGE_TRUSTCA_STR, - oc_cred_read_credusage(OC_CREDUSAGE_TRUSTCA)); - EXPECT_STREQ(OC_CREDUSAGE_IDENTITY_CERT_STR, - oc_cred_read_credusage(OC_CREDUSAGE_IDENTITY_CERT)); - EXPECT_STREQ(OC_CREDUSAGE_ROLE_CERT_STR, - oc_cred_read_credusage(OC_CREDUSAGE_ROLE_CERT)); - EXPECT_STREQ(OC_CREDUSAGE_MFG_TRUSTCA_STR, - oc_cred_read_credusage(OC_CREDUSAGE_MFG_TRUSTCA)); - EXPECT_STREQ(OC_CREDUSAGE_MFG_CERT_STR, - oc_cred_read_credusage(OC_CREDUSAGE_MFG_CERT)); + // root ca certificate + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int rootCredID = addRootCertificate(kDeviceID, rootKey); + ASSERT_LT(0, rootCredID); + + oc_sec_creds_t *device_creds = oc_sec_get_creds(kDeviceID); + ASSERT_NE(nullptr, device_creds); + + std::vector pem{}; + pem.resize(1024); + auto pemSize = oc_cred_serialize(device_creds->creds, nullptr, nullptr, + &pem[0], pem.size()); + ASSERT_LT(0, pemSize); + pem.resize(pemSize); + + std::vector too_small{}; + too_small.resize(pemSize - 1); + EXPECT_EQ(-1, oc_cred_serialize(device_creds->creds, nullptr, nullptr, + &too_small[0], too_small.size())); } -#endif /* OC_PKI */ +#endif /* OC_PKI && (OC_DYNAMIC_ALLOCATION || OC_TEST) */ #endif /* OC_SECURITY */ diff --git a/security/unittest/credutiltest.cpp b/security/unittest/credutiltest.cpp new file mode 100644 index 000000000..dc5285ff3 --- /dev/null +++ b/security/unittest/credutiltest.cpp @@ -0,0 +1,346 @@ +/****************************************************************** + * + * Copyright 2022 Daniel Adam, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ + +#ifdef OC_SECURITY + +#include "oc_cred.h" +#include "oc_helpers.h" +#include "port/oc_random.h" +#include "security/oc_cred_internal.h" +#include "security/oc_cred_util_internal.h" +#include "tests/gtest/Utility.h" +#include "util/oc_macros_internal.h" +#include "util/oc_mmem_internal.h" + +#include +#include +#include +#include +#include + +class TestCredUtil : public testing::Test { +public: + static void SetUpTestCase() { oc_random_init(); } + static void TearDownTestCase() { oc_random_destroy(); } +}; + +TEST_F(TestCredUtil, DataIsEqualToEncodedData) +{ + oc_cred_data_t cd_null{}; + oc_sec_encoded_data_t sed_null{}; + cd_null.encoding = OC_ENCODING_PEM; + sed_null.encoding = OC_ENCODING_PEM; + EXPECT_TRUE(oc_cred_data_is_equal_to_encoded_data(cd_null, sed_null)); + cd_null.encoding = OC_ENCODING_RAW; + sed_null.encoding = OC_ENCODING_RAW; + EXPECT_TRUE(oc_cred_data_is_equal_to_encoded_data(cd_null, sed_null)); + + std::string data1{ "leet" }; + oc_cred_data_t cd1{ OC_MMEM(data1.data(), data1.length() + 1, nullptr), + OC_ENCODING_PEM }; + oc_sec_encoded_data_t sed1{ (const uint8_t *)data1.data(), data1.length(), + OC_ENCODING_PEM }; + EXPECT_TRUE(oc_cred_data_is_equal_to_encoded_data(cd1, sed1)); + + EXPECT_FALSE(oc_cred_data_is_equal_to_encoded_data(cd_null, sed1)); + EXPECT_FALSE(oc_cred_data_is_equal_to_encoded_data(cd1, sed_null)); + + std::string data2{ "aaaa" }; + oc_cred_data_t cd2{ OC_MMEM(data2.data(), data2.length() + 1, nullptr), + OC_ENCODING_PEM }; + oc_sec_encoded_data_t sed2{ (const uint8_t *)data2.data(), data2.length(), + OC_ENCODING_PEM }; + EXPECT_FALSE(oc_cred_data_is_equal_to_encoded_data(cd2, sed1)); + EXPECT_FALSE(oc_cred_data_is_equal_to_encoded_data(cd1, sed2)); + + std::string data3{ "42" }; + oc_cred_data_t cd3{ OC_MMEM(data3.data(), data3.length() + 1, nullptr), + OC_ENCODING_PEM }; + oc_sec_encoded_data_t sed3{ (const uint8_t *)data3.data(), data3.length(), + OC_ENCODING_PEM }; + EXPECT_FALSE(oc_cred_data_is_equal_to_encoded_data(cd3, sed1)); + EXPECT_FALSE(oc_cred_data_is_equal_to_encoded_data(cd2, sed3)); +} + +TEST_F(TestCredUtil, HasTag) +{ + oc_sec_cred_t cred_null{}; + EXPECT_TRUE(oc_cred_has_tag(&cred_null, OC_STRING_VIEW_NULL)); + EXPECT_TRUE(oc_cred_has_tag(&cred_null, oc_string_view("", 0))); + + std::string tag1{ "tag" }; + std::string tag2{ "xxx" }; + EXPECT_FALSE( + oc_cred_has_tag(&cred_null, oc_string_view(tag1.data(), tag1.length()))); + EXPECT_FALSE( + oc_cred_has_tag(&cred_null, oc_string_view(tag2.data(), tag2.length()))); + + oc_sec_cred_t cred1{}; + cred1.tag = OC_MMEM(&tag1[0], tag1.length() + 1, nullptr); + EXPECT_TRUE(oc_cred_has_tag(&cred1, oc_string_view2(&cred1.tag))); + EXPECT_FALSE(oc_cred_has_tag(&cred1, OC_STRING_VIEW_NULL)); + EXPECT_FALSE( + oc_cred_has_tag(&cred1, oc_string_view(tag2.data(), tag2.length()))); +} + +TEST_F(TestCredUtil, IsDuplicate) +{ + oc_sec_cred_t cred{}; + cred.credtype = OC_CREDTYPE_CERT; + oc_gen_uuid(&cred.subjectuuid); + std::string credtag{ "tag1" }; + cred.tag = OC_MMEM(&credtag[0], credtag.length() + 1, nullptr); + + auto creddata = oc::GetVector("leet", true); + cred.privatedata = { OC_MMEM(creddata.data(), creddata.size(), nullptr), + OC_ENCODING_PEM }; + oc_sec_encoded_data_t credprivatedata{ creddata.data(), creddata.size() - 1, + OC_ENCODING_PEM }; +#ifdef OC_PKI + auto credpublicdata = oc::GetVector("based"); + cred.publicdata = { OC_MMEM(credpublicdata.data(), credpublicdata.size(), + nullptr), + OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata{ credpublicdata.data(), + credpublicdata.size(), OC_ENCODING_RAW }; + oc_sec_credusage_t credusage = OC_CREDUSAGE_IDENTITY_CERT; + cred.credusage = credusage; +#else /* !OC_PKI */ + oc_sec_encoded_data_t publicdata{}; + oc_sec_credusage_t credusage{}; +#endif /* OC_PKI */ + + EXPECT_TRUE(oc_cred_is_duplicate(&cred, cred.credtype, cred.subjectuuid, + oc_string_view2(&cred.tag), credprivatedata, + publicdata, credusage)); + + oc_sec_credtype_t credtype = OC_CREDTYPE_PSK; + ASSERT_NE(cred.credtype, credtype); + EXPECT_FALSE(oc_cred_is_duplicate(&cred, OC_CREDTYPE_PSK, cred.subjectuuid, + oc_string_view2(&cred.tag), credprivatedata, + publicdata, credusage)); + oc_uuid_t subjectuuid{}; + subjectuuid.id[0] = '*'; + ASSERT_FALSE(oc_uuid_is_equal(cred.subjectuuid, subjectuuid)); + EXPECT_FALSE(oc_cred_is_duplicate(&cred, cred.credtype, subjectuuid, + oc_string_view2(&cred.tag), credprivatedata, + publicdata, credusage)); + + std::string tag{ "tag2" }; + EXPECT_FALSE(oc_cred_is_duplicate(&cred, cred.credtype, cred.subjectuuid, + oc_string_view(tag.c_str(), tag.length()), + credprivatedata, publicdata, credusage)); + + auto data = oc::GetVector("42", true); + oc_sec_encoded_data_t sedData{ data.data(), data.size() - 1, + OC_ENCODING_PEM }; + EXPECT_FALSE(oc_cred_is_duplicate(&cred, cred.credtype, cred.subjectuuid, + oc_string_view2(&cred.tag), sedData, + publicdata, credusage)); + +#ifdef OC_PKI + EXPECT_FALSE(oc_cred_is_duplicate(&cred, cred.credtype, cred.subjectuuid, + oc_string_view2(&cred.tag), credprivatedata, + sedData, credusage)); + + oc_sec_credusage_t usage = OC_CREDUSAGE_MFG_CERT; + EXPECT_FALSE(oc_cred_is_duplicate(&cred, cred.credtype, cred.subjectuuid, + oc_string_view2(&cred.tag), credprivatedata, + publicdata, usage)); +#endif /* OC_PKI */ +} + +TEST_F(TestCredUtil, SetSubject) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + std::array uuid_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size())); + + oc_uuid_t subject{}; + ASSERT_TRUE(oc_sec_cred_set_subject(uuid_str.data(), + OC_CREDUSAGE_IDENTITY_CERT, &subject)); + EXPECT_TRUE(oc_uuid_is_equal(uuid, subject)); + + subject = {}; + ASSERT_TRUE(oc_sec_cred_set_subject("*", OC_CREDUSAGE_TRUSTCA, &subject)); + EXPECT_EQ('*', subject.id[0]); +} + +TEST_F(TestCredUtil, SetNullSubject) +{ + oc_uuid_t subject{}; + EXPECT_FALSE(oc_sec_cred_set_subject(nullptr, OC_CREDUSAGE_NULL, &subject)); + EXPECT_FALSE( + oc_sec_cred_set_subject(nullptr, OC_CREDUSAGE_IDENTITY_CERT, &subject)); + EXPECT_FALSE( + oc_sec_cred_set_subject(nullptr, OC_CREDUSAGE_MFG_TRUSTCA, &subject)); + EXPECT_FALSE( + oc_sec_cred_set_subject(nullptr, OC_CREDUSAGE_MFG_CERT, &subject)); + + EXPECT_TRUE( + oc_sec_cred_set_subject(nullptr, OC_CREDUSAGE_ROLE_CERT, &subject)); + EXPECT_EQ('*', subject.id[0]); +} + +TEST_F(TestCredUtil, CredTypeString) +{ + std::vector credtypes{ + OC_CREDTYPE_NULL, + OC_CREDTYPE_PSK, + OC_CREDTYPE_CERT, + OC_CREDTYPE_OSCORE, + OC_CREDTYPE_OSCORE_MCAST_CLIENT, + OC_CREDTYPE_OSCORE_MCAST_SERVER, + }; + std::vector credtypesStrings{ + "Unknown", OC_CREDTYPE_PSK_STR, OC_CREDTYPE_CERT_STR, + "Unknown", "Unknown", "Unknown", + }; + + for (size_t i = 0; i < credtypes.size(); ++i) { + EXPECT_STREQ(credtypesStrings[i].c_str(), + oc_cred_credtype_string(credtypes[i])); + } + + EXPECT_STREQ("Unknown", oc_cred_credtype_string( + std::numeric_limits::max())); +} + +TEST_F(TestCredUtil, ParseEncoding) +{ + std::vector encodings{ + OC_ENCODING_BASE64, + OC_ENCODING_RAW, +#ifdef OC_PKI + OC_ENCODING_PEM, +#endif /* OC_PKI */ + OC_ENCODING_HANDLE, + }; + std::vector encStrings{ + OC_ENCODING_BASE64_STR, + OC_ENCODING_RAW_STR, +#ifdef OC_PKI + OC_ENCODING_PEM_STR, +#endif /* OC_PKI */ + OC_ENCODING_HANDLE_STR, + }; + + oc_string_t enc{}; + for (size_t i = 0; i < encodings.size(); ++i) { + std::string encoding = encStrings[i]; + oc_set_string(&enc, encoding.c_str(), encoding.length()); + EXPECT_EQ(encodings[i], oc_cred_parse_encoding(&enc)); + } + + oc_set_string(&enc, nullptr, 0); + EXPECT_EQ(OC_ENCODING_UNSUPPORTED, oc_cred_parse_encoding(&enc)); + std::string invalid{}; + oc_set_string(&enc, invalid.c_str(), invalid.length()); + EXPECT_EQ(OC_ENCODING_UNSUPPORTED, oc_cred_parse_encoding(&enc)); + invalid = "invalid"; + oc_set_string(&enc, invalid.c_str(), invalid.length()); + EXPECT_EQ(OC_ENCODING_UNSUPPORTED, oc_cred_parse_encoding(&enc)); + + oc_free_string(&enc); +} + +TEST_F(TestCredUtil, ReadEncoding) +{ + std::vector encodings{ + OC_ENCODING_BASE64, + OC_ENCODING_RAW, +#ifdef OC_PKI + OC_ENCODING_PEM, +#endif /* OC_PKI */ + OC_ENCODING_HANDLE, + }; + std::vector encStrings{ + OC_ENCODING_BASE64_STR, + OC_ENCODING_RAW_STR, +#ifdef OC_PKI + OC_ENCODING_PEM_STR, +#endif /* OC_PKI */ + OC_ENCODING_HANDLE_STR, + }; + for (size_t i = 0; i < encodings.size(); ++i) { + EXPECT_STREQ(encStrings[i].c_str(), oc_cred_read_encoding(encodings[i])); + } + + EXPECT_STREQ("Unknown", oc_cred_read_encoding( + std::numeric_limits::max())); +} + +#ifdef OC_PKI + +TEST_F(TestCredUtil, ParseCredUsage) +{ + std::vector usages{ + OC_CREDUSAGE_TRUSTCA, OC_CREDUSAGE_IDENTITY_CERT, + OC_CREDUSAGE_ROLE_CERT, OC_CREDUSAGE_MFG_TRUSTCA, + OC_CREDUSAGE_MFG_CERT, + }; + std::vector usageStrings{ + OC_CREDUSAGE_TRUSTCA_STR, OC_CREDUSAGE_IDENTITY_CERT_STR, + OC_CREDUSAGE_ROLE_CERT_STR, OC_CREDUSAGE_MFG_TRUSTCA_STR, + OC_CREDUSAGE_MFG_CERT_STR, + }; + + oc_string_t usage{}; + for (size_t i = 0; i < usageStrings.size(); ++i) { + oc_set_string(&usage, usageStrings[i].c_str(), usageStrings[i].length()); + EXPECT_EQ(usages[i], oc_cred_parse_credusage(&usage)); + } + + oc_set_string(&usage, nullptr, 0); + EXPECT_EQ(OC_CREDUSAGE_NULL, oc_cred_parse_credusage(&usage)); + std::string invalid = ""; + oc_set_string(&usage, invalid.c_str(), invalid.length()); + EXPECT_EQ(OC_CREDUSAGE_NULL, oc_cred_parse_credusage(&usage)); + invalid = "invalid"; + oc_set_string(&usage, invalid.c_str(), invalid.length()); + EXPECT_EQ(OC_CREDUSAGE_NULL, oc_cred_parse_credusage(&usage)); + + oc_free_string(&usage); +} + +TEST_F(TestCredUtil, ReadCredUsage) +{ + std::vector usages{ + OC_CREDUSAGE_TRUSTCA, OC_CREDUSAGE_IDENTITY_CERT, + OC_CREDUSAGE_ROLE_CERT, OC_CREDUSAGE_MFG_TRUSTCA, + OC_CREDUSAGE_MFG_CERT, + }; + std::vector usageStrings{ + OC_CREDUSAGE_TRUSTCA_STR, OC_CREDUSAGE_IDENTITY_CERT_STR, + OC_CREDUSAGE_ROLE_CERT_STR, OC_CREDUSAGE_MFG_TRUSTCA_STR, + OC_CREDUSAGE_MFG_CERT_STR, + }; + + for (size_t i = 0; i < usages.size(); ++i) { + EXPECT_STREQ(usageStrings[i].c_str(), oc_cred_read_credusage(usages[i])); + } + + EXPECT_STREQ("None", oc_cred_read_credusage(OC_CREDUSAGE_NULL)); + EXPECT_STREQ("None", oc_cred_read_credusage( + std::numeric_limits::max())); +} + +#endif /* OC_PKI */ + +#endif /* OC_SECURITY */ diff --git a/security/unittest/obt_certstest.cpp b/security/unittest/obt_certstest.cpp index baeb3f158..13d56ca32 100644 --- a/security/unittest/obt_certstest.cpp +++ b/security/unittest/obt_certstest.cpp @@ -631,7 +631,7 @@ TEST_F(TestObtCertsWithDevice, RootCertificateCredential) size_t device = 0; int credid = oc_obt_generate_self_signed_root_cert(cert_data, device); - EXPECT_LT(0, credid); + ASSERT_LT(0, credid); oc_sec_cred_t *cred = oc_sec_get_cred_by_credid(credid, device); EXPECT_NE(nullptr, cred); diff --git a/security/unittest/pkitest.cpp b/security/unittest/pkitest.cpp index 934ace6d6..a562fd474 100644 --- a/security/unittest/pkitest.cpp +++ b/security/unittest/pkitest.cpp @@ -18,13 +18,247 @@ #if defined(OC_SECURITY) && defined(OC_PKI) +#include "api/oc_core_res_internal.h" +#include "api/oc_ri_internal.h" +#include "api/oc_runtime_internal.h" +#include "oc_api.h" +#include "oc_cred.h" #include "oc_pki.h" #include "oc_config.h" +#include "port/oc_network_event_handler_internal.h" +#include "security/oc_certs_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_pki_internal.h" +#include "security/oc_security_internal.h" +#include "security/oc_svr_internal.h" +#include "tests/gtest/Device.h" #include "tests/gtest/PKI.h" -#include +#ifdef OC_HAS_FEATURE_PUSH +#include "api/oc_push_internal.h" +#endif /* OC_HAS_FEATURE_PUSH */ + +#include + #include +#include +#include + +static constexpr size_t kDeviceID{ 0 }; + +class TestPKI : public testing::Test { +public: + static void SetUpTestCase() + { + oc_network_event_handler_mutex_init(); + oc_runtime_init(); + oc_ri_init(); + oc_core_init(); + ASSERT_EQ(0, oc_add_device(oc::DefaultDevice.uri.c_str(), + oc::DefaultDevice.rt.c_str(), + oc::DefaultDevice.name.c_str(), + oc::DefaultDevice.spec_version.c_str(), + oc::DefaultDevice.data_model_version.c_str(), + nullptr, nullptr)); + oc_sec_svr_create(); + oc_mbedtls_init(); + } + + static void TearDownTestCase() + { + oc_sec_svr_free(); +#ifdef OC_HAS_FEATURE_PUSH + oc_push_free(); +#endif /* OC_HAS_FEATURE_PUSH */ + oc_connectivity_shutdown(kDeviceID); + oc_core_shutdown(); + oc_ri_shutdown(); + oc_runtime_shutdown(); + oc_network_event_handler_mutex_destroy(); + } + + void TearDown() override + { + oc_sec_cred_clear(kDeviceID, nullptr, nullptr); + } + +#ifdef OC_DYNAMIC_ALLOCATION + static size_t countCreds(size_t device) + { + const oc_sec_creds_t *device_creds = oc_sec_get_creds(device); + if (device_creds == nullptr) { + return 0; + } + + size_t count = 0; + oc_cred_iterate( + device_creds->creds, + [](const oc_sec_cred_t *, void *data) { + ++(*static_cast(data)); + return true; + }, + &count); + return count; + } +#endif /* OC_DYNAMIC_ALLOCATION */ +}; + +TEST_F(TestPKI, AddIdentityCertificate_FailInvalidInput) +{ + EXPECT_EQ(-1, oc_pki_add_identity_cert(kDeviceID, nullptr, 0, nullptr, 0)); +} + +TEST_F(TestPKI, AddTrustAnchor_FailInvalidInput) +{ + EXPECT_EQ(-1, oc_pki_add_trust_anchor(kDeviceID, nullptr, 0)); +} + +#ifdef OC_DYNAMIC_ALLOCATION + +TEST_F(TestPKI, AddIdentityCertificate) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + + auto pem = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + ASSERT_FALSE(pem.empty()); + oc::pki::KeyParser parser{}; + auto keyPem = + parser.GetPrivateKey(rootKey.private_key.data(), rootKey.private_key_size); + ASSERT_FALSE(keyPem.empty()); + EXPECT_NE(-1, oc_pki_add_identity_cert(kDeviceID, pem.data(), pem.size(), + keyPem.data(), keyPem.size())); + + auto pem2 = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + ASSERT_FALSE(pem2.empty()); + // send size without nul-terminator for pem2 and keyPem + EXPECT_NE(-1, + oc_pki_add_identity_cert(kDeviceID, pem2.data(), pem2.size() - 1, + keyPem.data(), keyPem.size() - 1)); + + auto pem3 = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + ASSERT_FALSE(pem3.empty()); + // use DER format for key + EXPECT_NE(-1, oc_pki_add_identity_cert( + kDeviceID, pem3.data(), pem3.size() - 1, + rootKey.private_key.data(), rootKey.private_key_size)); +} + +TEST_F(TestPKI, AddIdentityCertificate_FailInvalidDevice) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + + auto pem = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + EXPECT_EQ(-1, oc_pki_add_identity_cert(/*device*/ 42, pem.data(), pem.size(), + rootKey.private_key.data(), + rootKey.private_key_size)); +} + +TEST_F(TestPKI, AddIdentityCertificate_Duplicate) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + + auto pem = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + int credid = oc_pki_add_identity_cert(kDeviceID, pem.data(), pem.size(), + rootKey.private_key.data(), + rootKey.private_key_size); + EXPECT_NE(-1, credid); + ASSERT_EQ(1, TestPKI::countCreds(kDeviceID)); + + EXPECT_EQ(credid, oc_pki_add_identity_cert(kDeviceID, pem.data(), pem.size(), + rootKey.private_key.data(), + rootKey.private_key_size)); + EXPECT_EQ(1, TestPKI::countCreds(kDeviceID)); +} + +TEST_F(TestPKI, AddIdentityCertificate_FailCorruptedKey) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + + auto pem = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + ASSERT_FALSE(pem.empty()); + oc::pki::KeyParser parser{}; + auto keyPem = + parser.GetPrivateKey(rootKey.private_key.data(), rootKey.private_key_size); + ASSERT_FALSE(keyPem.empty()); + constexpr std::string_view label{ "-----BEGIN EC PRIVATE KEY-----" }; + keyPem.insert(keyPem.begin() + label.length(), + { 'l', 'e', 'e', 't', '4', '2' }); + ASSERT_TRUE(oc_certs_is_PEM(keyPem.data(), keyPem.size())); + + EXPECT_EQ(-1, oc_pki_add_identity_cert(kDeviceID, pem.data(), pem.size(), + keyPem.data(), keyPem.size())); +} + +TEST_F(TestPKI, AddIdentityCertificate_FailCorruptedCertifiate) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + + auto pem = oc::pki::GeneratIdentityCertificate(rootKey, identKey); + ASSERT_FALSE(pem.empty()); + constexpr std::string_view label{ "-----BEGIN CERTIFICATE-----" }; + pem.insert(pem.begin() + label.length(), { 'l', 'e', 'e', 't', '4', '2' }); + ASSERT_TRUE(oc_certs_is_PEM(pem.data(), pem.size())); + + EXPECT_EQ(-1, oc_pki_add_identity_cert(kDeviceID, pem.data(), pem.size(), + rootKey.private_key.data(), + rootKey.private_key_size)); +} + +TEST_F(TestPKI, AddTrustAnchor) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + auto pem = oc::pki::GenerateRootCertificate(rootKey); + ASSERT_FALSE(pem.empty()); + EXPECT_NE(-1, oc_pki_add_trust_anchor(kDeviceID, pem.data(), pem.size())); + + auto pem2 = oc::pki::GenerateRootCertificate(rootKey); + ASSERT_FALSE(pem2.empty()); + // send size without nul-terminator for pem2 + EXPECT_NE(-1, + oc_pki_add_trust_anchor(kDeviceID, pem2.data(), pem2.size() - 1)); +} + +TEST_F(TestPKI, AddTrustAnchor_FailInvalidDevice) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + auto pem = oc::pki::GenerateRootCertificate(rootKey); + ASSERT_FALSE(pem.empty()); + + EXPECT_EQ(-1, oc_pki_add_trust_anchor(/*device*/ 42, pem.data(), pem.size())); +} + +// the same certificate should not be added to the same device twice +TEST_F(TestPKI, AddTrustAnchor_Duplicate) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + auto pem = oc::pki::GenerateRootCertificate(rootKey); + ASSERT_FALSE(pem.empty()); + int credid = oc_pki_add_trust_anchor(kDeviceID, pem.data(), pem.size()); + EXPECT_NE(-1, credid); + ASSERT_EQ(1, TestPKI::countCreds(kDeviceID)); + + EXPECT_EQ(credid, oc_pki_add_trust_anchor(kDeviceID, pem.data(), pem.size())); + EXPECT_EQ(1, TestPKI::countCreds(kDeviceID)); +} + +TEST_F(TestPKI, AddTrustAnchor_FailCorruptedCertifiate) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + auto pem = oc::pki::GenerateRootCertificate(rootKey); + ASSERT_FALSE(pem.empty()); + constexpr std::string_view label{ "-----BEGIN CERTIFICATE-----" }; + pem.insert(pem.begin() + label.length(), { 'l', 'e', 'e', 't', '4', '2' }); + ASSERT_TRUE(oc_certs_is_PEM(pem.data(), pem.size())); + + EXPECT_EQ(-1, oc_pki_add_trust_anchor(kDeviceID, pem.data(), pem.size())); +} + +#endif /* OC_DYNAMIC_ALLOCATION */ class TestPKIPK : public testing::Test { public: diff --git a/security/unittest/rolestest.cpp b/security/unittest/rolestest.cpp index 5860f4ff6..ffd856e77 100644 --- a/security/unittest/rolestest.cpp +++ b/security/unittest/rolestest.cpp @@ -28,6 +28,7 @@ #include "port/oc_log_internal.h" #include "security/oc_certs_generate_internal.h" #include "security/oc_certs_internal.h" +#include "security/oc_cred_util_internal.h" #include "security/oc_obt_internal.h" #include "security/oc_roles_internal.h" #include "security/oc_security_internal.h" diff --git a/tests/gtest/PKI.cpp b/tests/gtest/PKI.cpp index 223b7f11d..d1e910ef5 100644 --- a/tests/gtest/PKI.cpp +++ b/tests/gtest/PKI.cpp @@ -18,14 +18,18 @@ #ifdef OC_PKI -#include "PKI.h" #include "oc_pki.h" +#include "PKI.h" #include "port/oc_log_internal.h" +#include "security/oc_certs_internal.h" +#include "security/oc_entropy_internal.h" +#include #include #include #include #include +#include namespace oc::pki { @@ -50,6 +54,133 @@ ReadPem(const std::string &path) return data; } +#if defined(OC_DYNAMIC_ALLOCATION) || defined(OC_TEST) + +static constexpr std::string_view kRootSubjectName{ "IoTivity-Lite Test" }; +static const std::string kRootSubject{ "C=US, O=OCF, CN=" + + std::string(kRootSubjectName) }; +static const std::vector kPersonalizationString{ 'I', 'o', 'T' }; +// 12/31/2029 23:59:59 to seconds since epoch +static constexpr int64_t kNotAfter{ 1893455999 }; + +std::vector +GenerateCertificate(const oc_certs_generate_t &generate) +{ + std::vector cert_buf{}; + cert_buf.resize(4096, '\0'); + int err = oc_certs_generate(&generate, cert_buf.data(), cert_buf.size()); + EXPECT_EQ(0, err); + if (err != 0) { + return {}; + } + + auto it = std::find(cert_buf.begin(), cert_buf.end(), + static_cast('\0')); + size_t data_len = + std::distance(cert_buf.begin(), it) + 1; // size with NULL terminator + if (cert_buf.end() == it || !oc_certs_is_PEM(&cert_buf[0], data_len)) { + return {}; + } + cert_buf.resize(data_len); + return cert_buf; +} + +std::vector +GenerateRootCertificate(const oc::keypair_t &kp) +{ + oc_certs_generate_t root_cert{}; + root_cert.personalization_string = { kPersonalizationString.data(), + kPersonalizationString.size() }; + root_cert.serial_number_size = 20; + root_cert.validity.not_before = oc_certs_timestamp_now(); + root_cert.validity.not_after = { kNotAfter, 0, 0 }; + root_cert.subject.name = kRootSubject.c_str(); + root_cert.subject.public_key = { kp.public_key.data(), kp.public_key_size }; + root_cert.subject.private_key = { kp.private_key.data(), + kp.private_key_size }; + root_cert.signature_md = MBEDTLS_MD_SHA256; + root_cert.is_CA = true; + return GenerateCertificate(root_cert); +} + +std::vector +GeneratIdentityCertificate(const oc::keypair_t &kp, + const oc::keypair_t &issuer_kp) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + std::array subject{}; + if (!oc_certs_encode_CN_with_UUID(&uuid, subject.data(), subject.size())) { + return {}; + } + + oc_certs_generate_t identity_cert{}; + identity_cert.personalization_string = { kPersonalizationString.data(), + kPersonalizationString.size() }; + identity_cert.serial_number_size = 20; + identity_cert.validity.not_before = oc_certs_timestamp_now(); + identity_cert.validity.not_after = { kNotAfter, 0, 0 }; + identity_cert.subject.name = subject.data(); + identity_cert.subject.public_key = { kp.public_key.data(), + kp.public_key_size }; + identity_cert.issuer.name = kRootSubject.c_str(); + identity_cert.issuer.private_key = { issuer_kp.private_key.data(), + issuer_kp.private_key_size }; + identity_cert.signature_md = MBEDTLS_MD_SHA256; + return GenerateCertificate(identity_cert); +} + +#endif /* OC_DYNAMIC_ALLOCATION || OC_TEST */ + +KeyParser::KeyParser() +{ + mbedtls_entropy_init(&entropy_ctx_); + oc_entropy_add_source(&entropy_ctx_); + mbedtls_ctr_drbg_init(&ctr_drbg_ctx_); + std::string pers = "test"; + if (mbedtls_ctr_drbg_seed( + &ctr_drbg_ctx_, mbedtls_entropy_func, &entropy_ctx_, + reinterpret_cast(pers.c_str()), + pers.length()) != 0) { + throw std::string("failed to initialize entropy function"); + } +} + +KeyParser::~KeyParser() +{ + mbedtls_entropy_free(&entropy_ctx_); + mbedtls_ctr_drbg_free(&ctr_drbg_ctx_); +} + +std::vector +KeyParser::GetPrivateKey(const unsigned char *key, size_t keylen) +{ + mbedtls_pk_context pk; + mbedtls_pk_init(&pk); + if (mbedtls_pk_parse_key(&pk, key, keylen, nullptr, 0, + mbedtls_ctr_drbg_random, &ctr_drbg_ctx_) != 0) { + mbedtls_pk_free(&pk); + return {}; + } + + std::vector pem{}; + pem.resize(1024); + if (mbedtls_pk_write_key_pem(&pk, &pem[0], pem.size()) != 0) { + mbedtls_pk_free(&pk); + return {}; + } + mbedtls_pk_free(&pk); + + auto it = std::find(pem.begin(), pem.end(), static_cast('\0')); + if (pem.end() == it) { + return {}; + } + size_t dataSize = + std::distance(pem.begin(), it) + 1; // include null terminator + pem.resize(dataSize); + return pem; +} + PemData::PemData(const std::string &path) : path_{ path } { diff --git a/tests/gtest/PKI.h b/tests/gtest/PKI.h index ae2498037..9a43dd2b5 100644 --- a/tests/gtest/PKI.h +++ b/tests/gtest/PKI.h @@ -20,7 +20,13 @@ #ifdef OC_PKI -#include +#include "oc_pki.h" +#include "security/oc_certs_generate_internal.h" +#include "tests/gtest/KeyPair.h" + +#include +#include +#include #include #include @@ -29,6 +35,29 @@ namespace oc::pki { std::vector ReadPem(const std::string &path); +#if defined(OC_DYNAMIC_ALLOCATION) || defined(OC_TEST) +std::vector GenerateCertificate( + const oc_certs_generate_t &generate); + +std::vector GenerateRootCertificate(const oc::keypair_t &kp); + +std::vector GeneratIdentityCertificate( + const oc::keypair_t &kp, const oc::keypair_t &issuer_kp); +#endif /* OC_DYNAMIC_ALLOCATION || OC_TEST */ + +class KeyParser { +public: + KeyParser(); + ~KeyParser(); + + std::vector GetPrivateKey(const unsigned char *key, + size_t keylen); + +private: + mbedtls_ctr_drbg_context ctr_drbg_ctx_{}; + mbedtls_entropy_context entropy_ctx_{}; +}; + class PemData { public: PemData(const std::string &path); diff --git a/tests/gtest/tls/DTLS.cpp b/tests/gtest/tls/DTLS.cpp index 443f7506f..7efcb921a 100644 --- a/tests/gtest/tls/DTLS.cpp +++ b/tests/gtest/tls/DTLS.cpp @@ -32,17 +32,15 @@ std::optional AddPresharedKey(size_t device, const PreSharedKey &psk) { oc_uuid_t *uuid = oc_core_get_device_id(device); - IdentityHint hint{}; - std::copy(std::begin(uuid->id), std::end(uuid->id), std::begin(hint)); std::array uuid_str{}; oc_uuid_to_str(uuid, uuid_str.data(), uuid_str.size()); - if (oc_sec_add_new_cred( - device, false, nullptr, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - uuid_str.data(), { psk.data(), psk.size(), OC_ENCODING_RAW }, - { nullptr, 0, OC_ENCODING_UNSUPPORTED }, { nullptr, 0 }, { nullptr, 0 }, - { nullptr, 0 }, nullptr) == -1) { + if (oc_sec_add_new_psk_cred(device, uuid_str.data(), + { psk.data(), psk.size(), OC_ENCODING_RAW }, + OC_STRING_VIEW_NULL) == -1) { return {}; } + IdentityHint hint{}; + std::copy(std::begin(uuid->id), std::end(uuid->id), std::begin(hint)); return hint; }