diff --git a/.github/workflows/sonar-cloud-analysis.yml b/.github/workflows/sonar-cloud-analysis.yml index e8c89ac4a6..c3c9936655 100644 --- a/.github/workflows/sonar-cloud-analysis.yml +++ b/.github/workflows/sonar-cloud-analysis.yml @@ -28,7 +28,7 @@ jobs: # cloud (ipv4+tcp) on, collection create on, push on, rfotm on - build_args: "-DOC_CLOUD_ENABLED=ON -DOC_COLLECTIONS_IF_CREATE_ENABLED=ON -DOC_PUSH_ENABLED=ON -DOC_DISCOVERY_RESOURCE_OBSERVABLE_ENABLED=ON -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON" # ipv6 dns on, oscore off, rep realloc on - - build_args: "-DOC_DNS_LOOKUP_IPV6_ENABLED=ON -DOC_OSCORE_ENABLED=OFF -DOC_REP_ENCODING_REALLOC=ON" + - build_args: "-DOC_DNS_LOOKUP_IPV6_ENABLED=ON -DOC_OSCORE_ENABLED=OFF -DOC_REPRESENTATION_REALLOC_ENCODING_ENABLED=ON" # ipv4 on, tcp on, dynamic allocation off, rfotm on - build_args: "-DOC_IPV4_ENABLED=ON -DOC_TCP_ENABLED=ON -DOC_DYNAMIC_ALLOCATION_ENABLED=OFF -DOC_RESOURCE_ACCESS_IN_RFOTM_ENABLED=ON" # security off, cloud (ipv4+tcp), collection create on, push on, introspection IDD off diff --git a/api/client/unittest/clientcbtest.cpp b/api/client/unittest/clientcbtest.cpp index 7f1ff57487..eb8d9e9228 100644 --- a/api/client/unittest/clientcbtest.cpp +++ b/api/client/unittest/clientcbtest.cpp @@ -57,7 +57,7 @@ class TestClientCB : public testing::Test { static oc_discovery_flags_t discoveryHandler(const char *, const char *, oc_string_array_t, oc_interface_mask_t, - oc_endpoint_t *, + const oc_endpoint_t *, oc_resource_properties_t, void *) { TestClientCB::discoveryHandlerInvoked = true; @@ -66,7 +66,7 @@ class TestClientCB : public testing::Test { static oc_discovery_flags_t discoveryAllHandler( const char *, const char *, oc_string_array_t, oc_interface_mask_t, - oc_endpoint_t *, oc_resource_properties_t, bool, void *) + const oc_endpoint_t *, oc_resource_properties_t, bool, void *) { TestClientCB::discoveryAllHandlerInvoked = true; return OC_STOP_DISCOVERY; diff --git a/port/android/Makefile b/port/android/Makefile index 5db0b8853d..e2cd606396 100644 --- a/port/android/Makefile +++ b/port/android/Makefile @@ -140,7 +140,7 @@ ifeq ($(PKI),1) CTIMESTAMP=../../api/c-timestamp/timestamp_tm.c endif -CTIMESTAMP+=../../api/c-timestamp/timestamp_format.c ../../api/c-timestamp/timestamp_valid.c ../../api/c-timestamp/timestamp_parse.c +CTIMESTAMP+=../../api/c-timestamp/timestamp_compare.c ../../api/c-timestamp/timestamp_format.c ../../api/c-timestamp/timestamp_valid.c ../../api/c-timestamp/timestamp_parse.c SRC_PORT_COMMON:=$(wildcard ../../port/common/*.c) ifneq ($(MEMORY_TRACE),1) @@ -255,7 +255,7 @@ 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_validate.c oc_cred.c oc_csr.c oc_doxm.c oc_entropy.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_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 diff --git a/port/arduino/adapter/Makefile b/port/arduino/adapter/Makefile index 858e779dce..c087a02e28 100644 --- a/port/arduino/adapter/Makefile +++ b/port/arduino/adapter/Makefile @@ -39,7 +39,7 @@ ifeq ($(DYNAMIC),1) endif ifeq ($(SECURE),1) - SEC_SRC += $(addprefix $(ROOT_DIR)/security/, oc_acl.c oc_cred.c oc_certs.c oc_certs_validate.c oc_csr.c oc_doxm.c oc_entropy.c \ + 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) SRC += $(SEC_SRC) SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS}) diff --git a/port/esp32/main/CMakeLists.txt b/port/esp32/main/CMakeLists.txt index 5081fe6b2b..79b5dc89cb 100644 --- a/port/esp32/main/CMakeLists.txt +++ b/port/esp32/main/CMakeLists.txt @@ -157,6 +157,7 @@ if(CONFIG_SECURE) ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_ael.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_audit.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_certs.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_certs_generate.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_certs_validate.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_cred.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_csr.c diff --git a/port/linux/Makefile b/port/linux/Makefile index 8ba250f5b1..7acf0faf59 100644 --- a/port/linux/Makefile +++ b/port/linux/Makefile @@ -112,7 +112,7 @@ ifeq ($(PKI),1) CTIMESTAMP=../../api/c-timestamp/timestamp_tm.c endif -CTIMESTAMP+=../../api/c-timestamp/timestamp_format.c ../../api/c-timestamp/timestamp_valid.c ../../api/c-timestamp/timestamp_parse.c +CTIMESTAMP+=../../api/c-timestamp/timestamp_compare.c ../../api/c-timestamp/timestamp_format.c ../../api/c-timestamp/timestamp_valid.c ../../api/c-timestamp/timestamp_parse.c SRC_PORT_COMMON:=$(wildcard ../../port/common/*.c) ifneq ($(MEMORY_TRACE),1) @@ -254,7 +254,7 @@ endif ifneq ($(SECURE),0) - SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_validate.c oc_cred.c oc_csr.c oc_doxm.c oc_entropy.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_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}) diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj b/port/windows/vs2015/IoTivity-lite.vcxproj index c1961456f3..d1ac7e6f02 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj +++ b/port/windows/vs2015/IoTivity-lite.vcxproj @@ -427,6 +427,8 @@ + + diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj.filters b/port/windows/vs2015/IoTivity-lite.vcxproj.filters index f533e18969..1e7a072af9 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj.filters +++ b/port/windows/vs2015/IoTivity-lite.vcxproj.filters @@ -299,6 +299,12 @@ Security + + Security + + + Security + Security diff --git a/security/oc_certs.c b/security/oc_certs.c index 56c36a1404..6d99e5b33d 100644 --- a/security/oc_certs.c +++ b/security/oc_certs.c @@ -137,42 +137,6 @@ oc_sec_certs_ecp_group_id_is_allowed(mbedtls_ecp_group_id gid) (MBEDTLS_X509_ID_FLAG(gid) & g_allowed_ecp_grpids_mask) != 0; } -int -oc_certs_generate_serial_number(mbedtls_x509write_cert *crt, size_t size) -{ - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ctr_drbg_init(&ctr_drbg); - - mbedtls_entropy_context entropy; - mbedtls_entropy_init(&entropy); - oc_entropy_add_source(&entropy); - -#define PERSONALIZATION_DATA "IoTivity-Lite-Certificate_Serial_Number" - - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, - (const unsigned char *)PERSONALIZATION_DATA, - sizeof(PERSONALIZATION_DATA)); - -#undef PERSONALIZATION_DATA - - if (ret < 0) { - OC_ERR("error initializing RNG %d", ret); - return -1; - } - - mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); - - ret = mbedtls_mpi_fill_random(&crt->serial, size, mbedtls_ctr_drbg_random, - &ctr_drbg); - - if (ret < 0) { - OC_ERR("error generating random serial number for certificate %d", ret); - return -1; - } - - return 0; -} - bool oc_certs_is_PEM(const unsigned char *cert, size_t cert_len) { @@ -485,30 +449,6 @@ oc_certs_parse_CN_for_UUID(const unsigned char *cert, size_t cert_size, return ok; } -bool -oc_certs_encode_role(const oc_role_t *role, char *buf, size_t buf_len) -{ - char *buffer = buf; - size_t length = buf_len; - int ret = snprintf(buffer, length, "CN=%s", oc_string(role->role)); - if (ret < 0 || (size_t)ret >= length) { - OC_ERR("could not encode role"); - return false; - } - if (oc_string_len(role->authority) == 0) { - return true; - } - - buffer = buf + ret; - length -= ret; - ret = snprintf(buffer, length, ",OU=%s", oc_string(role->authority)); - if (ret < 0 || (size_t)ret >= length) { - OC_ERR("could not encode authority"); - return false; - } - return true; -} - static bool oc_certs_DN_is_CN(const mbedtls_x509_name *dn) { @@ -624,28 +564,6 @@ oc_certs_timestamp_now(void) return ts; } -bool -oc_certs_timestamp_format(timestamp_t ts, char *buffer, size_t buffer_size) -{ - assert(buffer != NULL); - - struct tm now_tm; - memset(&now_tm, 0, sizeof(struct tm)); - if (timestamp_to_tm_utc(&ts, &now_tm) == NULL) { - OC_ERR("cannot convert timestamp to string: invalid timestamp"); - return false; - } - - int ret = snprintf(buffer, buffer_size, "%d%02d%02d%02d%02d%02d", - now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, - now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec); - if (ret < 0 || (size_t)ret >= buffer_size) { - OC_ERR("cannot convert timestamp to string: buffer too small"); - return false; - } - return true; -} - uint64_t oc_certs_time_to_unix_timestamp(mbedtls_x509_time time) { diff --git a/security/oc_certs_generate.c b/security/oc_certs_generate.c new file mode 100644 index 0000000000..815016ed11 --- /dev/null +++ b/security/oc_certs_generate.c @@ -0,0 +1,449 @@ +/**************************************************************************** + * + * Copyright (c) 2023 plgd.dev s.r.o. + * 2019 Intel Corporation + * + * 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. + * + ****************************************************************************/ + +#include "oc_config.h" + +#if defined(OC_SECURITY) && defined(OC_PKI) && defined(OC_DYNAMIC_ALLOCATION) + +#include "port/oc_log_internal.h" +#include "security/oc_certs_generate_internal.h" +#include "security/oc_entropy_internal.h" +#include "util/oc_macros_internal.h" + +#include +#include +#include +#include +#include + +int +oc_certs_generate_serial_number(mbedtls_x509write_cert *crt, size_t size) +{ + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + + mbedtls_entropy_context entropy; + mbedtls_entropy_init(&entropy); + oc_entropy_add_source(&entropy); + +#define PERSONALIZATION_DATA "IoTivity-Lite-Certificate_Serial_Number" + + int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)PERSONALIZATION_DATA, + sizeof(PERSONALIZATION_DATA)); + +#undef PERSONALIZATION_DATA + + if (ret < 0) { + OC_ERR("error initializing RNG %d", ret); + return -1; + } + + mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); + + ret = mbedtls_mpi_fill_random(&crt->serial, size, mbedtls_ctr_drbg_random, + &ctr_drbg); + + if (ret < 0) { + OC_ERR("error generating random serial number for certificate %d", ret); + return -1; + } + return 0; +} + +bool +oc_certs_timestamp_format(timestamp_t ts, char *buffer, size_t buffer_size) +{ + assert(buffer != NULL); + + struct tm now_tm; + memset(&now_tm, 0, sizeof(struct tm)); + if (timestamp_to_tm_utc(&ts, &now_tm) == NULL) { + OC_ERR("cannot convert timestamp to string: invalid timestamp"); + return false; + } + + int ret = snprintf(buffer, buffer_size, "%d%02d%02d%02d%02d%02d", + now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, + now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec); + if (ret < 0 || (size_t)ret >= buffer_size) { + OC_ERR("cannot convert timestamp to string: buffer too small"); + return false; + } + return true; +} + +static bool +certs_validity_is_empty(oc_certs_validity_t validity) +{ + timestamp_t empty = { 0 }; + return timestamp_compare(&empty, &validity.not_before) == 0 && + timestamp_compare(&empty, &validity.not_after) == 0; +} + +static int +certs_validity_write(mbedtls_x509write_cert *cert, timestamp_t not_before, + timestamp_t not_after) +{ +#define OC_CERTS_TIMESTAMP_BUFFER_SIZE (15) + char nb[OC_CERTS_TIMESTAMP_BUFFER_SIZE] = { 0 }; + if (!oc_certs_timestamp_format(not_before, nb, OC_ARRAY_SIZE(nb))) { + return -1; + } + + char na[OC_CERTS_TIMESTAMP_BUFFER_SIZE] = { 0 }; + if (!oc_certs_timestamp_format(not_after, na, OC_ARRAY_SIZE(na))) { + return -1; + } + + int ret = mbedtls_x509write_crt_set_validity(cert, nb, na); + if (ret < 0) { + OC_ERR("error writing cert validity %d", ret); + return ret; + } + + OC_DBG("certificate validity not_before:%s not_after:%s", nb, na); + return 0; +} + +static int +certs_write_subject_and_issuer(mbedtls_x509write_cert *cert, + mbedtls_ctr_drbg_context *ctr_drbg, + oc_certs_subject_t subject, + mbedtls_pk_context *subject_pk, + oc_certs_issuer_t issuer, + mbedtls_pk_context *issuer_pk, bool is_CA) +{ + /* Subject */ + assert(subject.name != NULL); + OC_DBG("\tadding subject(%s)", subject.name); + int ret = mbedtls_x509write_crt_set_subject_name(cert, subject.name); + if (ret < 0) { + OC_ERR("error writing root cert subject name %d", ret); + return ret; + } + + assert(subject.public_key.value != NULL); + ret = mbedtls_pk_parse_public_key(subject_pk, subject.public_key.value, + subject.public_key.size); + if (ret < 0) { + OC_ERR("error parsing subjects' public key %d", ret); + return ret; + } + + if (is_CA) { + assert(subject.private_key.value != NULL); + + ret = mbedtls_pk_parse_key( + subject_pk, subject.private_key.value, subject.private_key.size, + /*pwd*/ NULL, /*pwd_len*/ 0, mbedtls_ctr_drbg_random, &ctr_drbg); + if (ret < 0) { + OC_ERR("error parsing subjects' private key %d", ret); + return ret; + } + } + mbedtls_x509write_crt_set_subject_key(cert, subject_pk); + + /* Issuer */ + if (is_CA) { + // for CA certificates, issuer is the same as subject + OC_DBG("\tadding CA issuer(%s)", subject.name); + + ret = mbedtls_x509write_crt_set_issuer_name(cert, subject.name); + if (ret < 0) { + OC_ERR("error writing CA certificate issuer name %d", ret); + return ret; + } + mbedtls_x509write_crt_set_issuer_key(cert, subject_pk); + } else { + assert(issuer.name != NULL); + OC_DBG("\tadding issuer(%s)", issuer.name); + + ret = mbedtls_x509write_crt_set_issuer_name(cert, issuer.name); + if (ret < 0) { + OC_ERR("error writing certificate issuer name %d", ret); + return ret; + } + + assert(issuer.private_key.value != NULL); + ret = mbedtls_pk_parse_key( + issuer_pk, issuer.private_key.value, issuer.private_key.size, + /*pwd*/ NULL, /*pwd_len*/ 0, mbedtls_ctr_drbg_random, &ctr_drbg); + if (ret < 0) { + OC_ERR("error parsing certificate issuers private key %d", ret); + return ret; + } + + mbedtls_x509write_crt_set_issuer_key(cert, issuer_pk); + } + return 0; +} + +static int +certs_write_key_usage(mbedtls_x509write_cert *cert, oc_certs_key_usage_t ku) +{ + if (ku.key_usage != 0) { + OC_DBG("\tadding keyUsage"); + int ret = mbedtls_x509write_crt_set_key_usage(cert, ku.key_usage); + if (ret < 0) { + OC_ERR("error writing certificate keyUsage: %d", ret); + return ret; + } + } + + if (ku.extended_key_usage.value != NULL) { + OC_DBG("\tadding extendedKeyUsage"); + int ret = mbedtls_x509write_crt_set_extension( + cert, MBEDTLS_OID_EXTENDED_KEY_USAGE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), 0, + ku.extended_key_usage.value, ku.extended_key_usage.size); + if (ret < 0) { + OC_ERR("error writing certificate extendedKeyUsage: %d", ret); + return ret; + } + } + return 0; +} + +void +oc_certs_free_encoded_roles(mbedtls_x509_general_names *general_names) +{ + while (general_names != NULL) { + mbedtls_x509_general_names *next = general_names->next; + mbedtls_asn1_free_named_data_list( + &general_names->general_name.name.directory_name); + free(general_names); + general_names = next; + } +} + +bool +oc_certs_encode_role(const oc_role_t *role, char *buf, size_t buf_len) +{ + char *buffer = buf; + size_t length = buf_len; + int ret = snprintf(buffer, length, "CN=%s", oc_string(role->role)); + if (ret < 0 || (size_t)ret >= length) { + OC_ERR("could not encode role"); + return false; + } + if (oc_string_len(role->authority) == 0) { + return true; + } + + buffer = buf + ret; + length -= ret; + ret = snprintf(buffer, length, ",OU=%s", oc_string(role->authority)); + if (ret < 0 || (size_t)ret >= length) { + OC_ERR("could not encode authority"); + return false; + } + return true; +} + +static mbedtls_x509_general_names * +certs_encode_role(const oc_role_t *role) +{ + char roleid[512]; + if (!oc_certs_encode_role(role, roleid, sizeof(roleid))) { + OC_ERR("error encoding roleid"); + return NULL; + } + /* A RoleId is encoded in a GeneralName that is of type directoryName into + * the GeneralNames SEQUEENCE. + */ + mbedtls_x509_general_names *name = + (mbedtls_x509_general_names *)calloc(1, sizeof(mbedtls_x509_general_names)); + if (name == NULL) { + OC_ERR("error allocating memory for GeneralName"); + return NULL; + } + name->general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME; + + int ret = mbedtls_x509_string_to_names( + &name->general_name.name.directory_name, roleid); + if (ret < 0) { + OC_ERR("error writing roleid to GeneralName %d", ret); + mbedtls_asn1_free_named_data_list(&name->general_name.name.directory_name); + free(name); + return NULL; + } + + return name; +} + +int +oc_certs_encode_roles(const oc_role_t *roles, + mbedtls_x509_general_names **general_names) +{ + mbedtls_x509_general_names *head = NULL; + mbedtls_x509_general_names *last = NULL; + + int count = 0; + while (roles != NULL) { + mbedtls_x509_general_names *name = certs_encode_role(roles); + if (name == NULL) { + oc_certs_free_encoded_roles(head); + return -1; + } + OC_DBG("encoding role[%d] (%s:%s)", count, oc_string(roles->role), + oc_string(roles->authority) != NULL ? oc_string(roles->authority) + : ""); + + if (head == NULL) { + head = name; + } + if (last != NULL) { + last->next = name; + } + last = name; + + ++count; + roles = roles->next; + } + + *general_names = head; + return count; +} + +static bool +certs_write_roles_to_subject_alt_names(mbedtls_x509write_cert *cert, + const oc_role_t *roles) +{ + mbedtls_x509_general_names *general_names = NULL; + int ret = oc_certs_encode_roles(roles, &general_names); + if (ret < 0) { + return false; + } + + ret = mbedtls_x509write_crt_set_subject_alt_names(cert, general_names); + if (ret < 0) { + OC_ERR("error writing subjectAlternativeName to cert %d", ret); + oc_certs_free_encoded_roles(general_names); + return false; + } + + oc_certs_free_encoded_roles(general_names); + return true; +} + +int +oc_certs_generate(oc_certs_generate_t data, unsigned char *buffer, + size_t buffer_size) +{ + assert(buffer != NULL); + OC_DBG("Generating certificate"); + + mbedtls_x509write_cert cert; + mbedtls_x509write_crt_init(&cert); + + mbedtls_entropy_context entropy; + mbedtls_entropy_init(&entropy); + oc_entropy_add_source(&entropy); + + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + + mbedtls_pk_context subject_pk; + mbedtls_pk_init(&subject_pk); + + mbedtls_pk_context issuer_pk; + mbedtls_pk_init(&issuer_pk); + + assert(data.personalization_string.value != NULL); + int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + data.personalization_string.value, + data.personalization_string.size); + if (ret < 0) { + OC_ERR("error initializing RNG %d", ret); + goto exit; + } + + mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); + + if (data.serial_number_size > 0) { + OC_DBG("\tadding serial number"); + /* SerialNumber */ + ret = oc_certs_generate_serial_number(&cert, data.serial_number_size); + if (ret < 0) { + goto exit; + } + } + + if (!certs_validity_is_empty(data.validity)) { + ret = certs_validity_write(&cert, data.validity.not_before, + data.validity.not_after); + if (ret < 0) { + goto exit; + } + } + + /* Version: v3 */ + mbedtls_x509write_crt_set_version(&cert, MBEDTLS_X509_CRT_VERSION_3); + /* signatureAlgorithm: ecdsa-with-SHA256 */ + mbedtls_x509write_crt_set_md_alg(&cert, data.signature_md); + + ret = + certs_write_subject_and_issuer(&cert, &ctr_drbg, data.subject, &subject_pk, + data.issuer, &issuer_pk, data.is_CA); + if (ret < 0) { + goto exit; + } + + int is_CA = data.is_CA ? 1 : 0; + int max_pathlen = data.is_CA ? -1 : 0; // -1 = unlimited + /* basicConstraints: cA = TRUE, pathLenConstraint = unlimited */ + ret = mbedtls_x509write_crt_set_basic_constraints(&cert, is_CA, max_pathlen); + if (ret < 0) { + OC_ERR("error writing certificate basicConstraints %d", ret); + goto exit; + } + + /* The subjectAlternativeName extension is populated with the GeneralNames + * SEQUENCE containing the Role. */ + if (data.roles != NULL && + !certs_write_roles_to_subject_alt_names(&cert, data.roles)) { + OC_ERR("error writing role cert subject alt names"); + ret = -1; + goto exit; + } + + ret = certs_write_key_usage(&cert, data.key_usage); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_x509write_crt_pem(&cert, buffer, buffer_size, + mbedtls_ctr_drbg_random, &ctr_drbg); + + if (ret < 0) { + OC_ERR("error serializing certificate into PEM %d", ret); + goto exit; + } + +exit: + mbedtls_pk_free(&issuer_pk); + mbedtls_pk_free(&subject_pk); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_x509write_crt_free(&cert); + return ret; +} + +#endif /* OC_SECURITY && OC_PKI && OC_DYNAMIC_ALLOCATION */ diff --git a/security/oc_certs_generate_internal.h b/security/oc_certs_generate_internal.h new file mode 100644 index 0000000000..003afd2dc1 --- /dev/null +++ b/security/oc_certs_generate_internal.h @@ -0,0 +1,155 @@ +/**************************************************************************** + * + * Copyright (c) 2023 plgd.dev s.r.o. + * 2019 Intel Corporation + * + * 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_CERTS_GENERATE_INTERNAL_H +#define OC_CERTS_GENERATE_INTERNAL_H + +#if defined(OC_SECURITY) && defined(OC_PKI) && defined(OC_DYNAMIC_ALLOCATION) + +#include "api/c-timestamp/timestamp.h" +#include "oc_role.h" +#include "util/oc_compiler.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Fill with serial number of the certificates with random byte string of given +/// size +int oc_certs_generate_serial_number(mbedtls_x509write_cert *crt, size_t size) + OC_NONNULL(); + +/** + * @brief Convert timestamp into a UTC timezone string expected by the x509 + * certificate in the notBefore and notAfter fields. + * + * @param ts timestamp to convert + * @param buffer output buffer (cannot be NULL) + * @param buffer_size size of the output buffer + * @return true on success + * @return false on failure + */ +bool oc_certs_timestamp_format(timestamp_t ts, char *buffer, size_t buffer_size) + OC_NONNULL(); + +/** + * @brief Encode role and authority into a nul-terminated C-String for a Common + * Name and Organizational Unit fields of a certificate. + * + * The Common Name (CN) component contains the role and the Organizational Unit + * (OU) component contains the authority. + * + * @param[in] role role and authority to encode (cannot be NULL) + * @param[out] buf output buffer to store the encoded data (cannot be NULL) + * @param[in] buf_len size of the output buffer + * @return true on success + * @return false on failure + */ +bool oc_certs_encode_role(const oc_role_t *role, char *buf, size_t buf_len) + OC_NONNULL(); + +/** + * @brief Encode linked list of role and authority pairs into linked list of + * mbedtls_x509_general_names* + * + * @param[in] roles linked list of role-authority pairs + * @param[out] general_names output pointer to store linked list of + * mbedtls_x509_general_names* (cannot be NULL, must be deallocated by + * oc_certs_free_encoded_roles) + * @return >=0 on success, number of encoded roles + * @return -1 on error + */ +int oc_certs_encode_roles(const oc_role_t *roles, + mbedtls_x509_general_names **general_names) + OC_NONNULL(2); + +/// @brief Deallocate a linked list of mbedtls_x509_general_names* +void oc_certs_free_encoded_roles(mbedtls_x509_general_names *general_names); + +typedef struct oc_certs_validity_t +{ + timestamp_t not_before; + timestamp_t not_after; +} oc_certs_validity_t; + +typedef struct oc_certs_buffer_t +{ + const uint8_t *value; + size_t size; +} oc_certs_buffer_t; + +typedef oc_certs_buffer_t oc_certs_key_t; + +typedef struct oc_certs_subject_t +{ + const char *name; ///< the subject name for a Certificate Subject names should + ///< contain a comma-separated list of OID types and + ///< values: e.g."C=UK,O=ARM,CN=mbed TLS Server 1" + oc_certs_key_t public_key; + oc_certs_key_t private_key; +} oc_certs_subject_t; + +typedef struct oc_certs_issuer_t +{ + const char *name; + oc_certs_key_t private_key; +} oc_certs_issuer_t; + +typedef struct oc_certs_key_usage_t +{ + unsigned int key_usage; + oc_certs_buffer_t extended_key_usage; +} oc_certs_key_usage_t; + +typedef struct oc_certs_generate_t +{ + oc_certs_buffer_t personalization_string; // cannot be empty + size_t serial_number_size; // number of bytes in serial number to generate + oc_certs_validity_t validity; // not before and not after timestamps + oc_certs_subject_t subject; + oc_certs_issuer_t issuer; + oc_certs_key_usage_t key_usage; + mbedtls_md_type_t signature_md; // MD algorithm to use for the signature + bool is_CA; // is a self-signed CA certificate + const oc_role_t *roles; // roles for a Role certificate +} oc_certs_generate_t; + +/** + * @brief Generate a certificate in PEM format. + * + * @param[in] data certificate data + * @param[out] buffer output buffer to store the generated certificate + * @param[in] buffer_size size of the output buffer + * @return >=0 on success, size of the generated certificate + * @return -1 on error + */ +int oc_certs_generate(oc_certs_generate_t data, unsigned char *buffer, + size_t buffer_size) OC_NONNULL(); + +#ifdef __cplusplus +} +#endif + +#endif /* OC_SECURITY & OC_PKI && OC_DYNAMIC_ALLOCATION */ + +#endif /* OC_CERTS_GENERATE_INTERNAL_H */ diff --git a/security/oc_certs_internal.h b/security/oc_certs_internal.h index 62081611b6..d8b798fb2e 100644 --- a/security/oc_certs_internal.h +++ b/security/oc_certs_internal.h @@ -38,10 +38,6 @@ extern "C" { /// Restore certificate and CSR configuration to default values. void oc_sec_certs_default(void); -/// Fill with serial number of the certificates with random byte string of given -/// size -int oc_certs_generate_serial_number(mbedtls_x509write_cert *crt, size_t size); - /// @brief Check that string is in PEM format. bool oc_certs_is_PEM(const unsigned char *cert, size_t cert_len); @@ -147,21 +143,6 @@ bool oc_certs_extract_CN_for_UUID(const mbedtls_x509_crt *cert, char *buffer, bool oc_certs_parse_CN_for_UUID(const unsigned char *cert, size_t cert_size, char *buffer, size_t buffer_size); -/** - * @brief Encode role and authority into a nul-terminated C-String for a Common - * Name and Organizational Unit fields of a certificate. - * - * The Common Name (CN) component contains the role and the Organizational Unit - * (OU) component contains the authority. - * - * @param[in] role role and authority to encode - * @param[out] buf output buffer to store the encoded data - * @param[in] buf_len size of the output buffer - * @return true on success - * @return false on failure - */ -bool oc_certs_encode_role(const oc_role_t *role, char *buf, size_t buf_len); - /** * @brief Extract the first role and authority pair from Common Name and * Organizational Unit fields of a certificate. @@ -189,19 +170,6 @@ timestamp_t oc_certs_timestamp_now(void); /// Convert mbedtls_x509_time to UNIX timestamp uint64_t oc_certs_time_to_unix_timestamp(mbedtls_x509_time time); -/** - * @brief Convert timestamp into a UTC timezone string expected by the x509 - * certificate in the notBefore and notAfter fields. - * - * @param ts timestamp to convert - * @param buffer output buffer (cannot be NULL) - * @param buffer_size size of the output buffer - * @return true on success - * @return false on failure - */ -bool oc_certs_timestamp_format(timestamp_t ts, char *buffer, - size_t buffer_size); - /// Serialize certificate chain to PEM string int oc_certs_serialize_chain_to_pem(const mbedtls_x509_crt *cert_chain, char *output_buffer, diff --git a/security/oc_cred.c b/security/oc_cred.c index c7b6b6efee..6ded4a92a0 100644 --- a/security/oc_cred.c +++ b/security/oc_cred.c @@ -62,8 +62,7 @@ static oc_sec_creds_t devices[OC_MAX_NUM_DEVICES]; #endif /* !OC_DYNAMIC_ALLOCATION */ #ifdef OC_PKI -static const char *allowed_roles[] = { "oic.role.owner" }; -static const int allowed_roles_num = sizeof(allowed_roles) / sizeof(char *); +static const char *g_allowed_roles[] = { "oic.role.owner" }; #endif /* OC_PKI */ // https://openconnectivity.org/specs/OCF_Security_Specification_v2.2.5.pdf @@ -393,22 +392,23 @@ oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, oc_sec_credtype_t credtype, } #ifdef OC_PKI -static int +static bool check_role_assertion(oc_sec_cred_t *cred) { - if (oc_string_len(cred->role.role) >= strlen("oic.role.") && - memcmp(oc_string(cred->role.role), "oic.role.", strlen("oic.role.")) == - 0) { - for (int i = 0; i < allowed_roles_num; i++) { - if (oc_sec_role_cred_match_role(cred, allowed_roles[i], - strlen(allowed_roles[i]), false)) { - return 0; + oc_string_view_t reserved = + oc_string_view("oic.role.", OC_CHAR_ARRAY_LEN("oic.role.")); + 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], + strlen(g_allowed_roles[i]), false)) { + return true; } } OC_ERR("oic.role.* roles assertion is prohibited"); - return -1; + return false; } - return 0; + return true; } static bool @@ -443,9 +443,11 @@ oc_sec_verify_role_cred(const oc_tls_peer_t *client, const uint8_t *publicdata) { if (credusage != OC_CREDUSAGE_ROLE_CERT) { + OC_ERR("failed to verify role certificate: invalid credusage"); return false; } if (!client) { + OC_ERR("failed to verify role certificate: invalid client"); return false; } if (client->public_key.size > 0 && @@ -453,10 +455,15 @@ oc_sec_verify_role_cred(const oc_tls_peer_t *client, oc_cast(client->public_key, uint8_t) + client->public_key.size - public_key_len, public_key_len) != 0) { + OC_ERR("failed to verify role certificate: invalid public key"); return false; } - return check_uuid_from_cert_raw(publicdata_size + 1, publicdata, - &client->uuid); + if (!check_uuid_from_cert_raw(publicdata_size + 1, publicdata, + &client->uuid)) { + OC_ERR("failed to verify role certificate: invalid uuid"); + return false; + } + return true; } #endif /* OC_PKI */ @@ -659,7 +666,7 @@ cred_create(cred_create_t create) return NULL; } - if (create.roles_resource && check_role_assertion(cred) < 0) { + if (create.roles_resource && !check_role_assertion(cred)) { oc_sec_free_role(cred, create.client); return NULL; } @@ -719,6 +726,24 @@ cred_create(cred_create_t create) return cred; } +#ifdef OC_PKI + +static bool +sec_match_cred_with_publicdata(const oc_sec_cred_t *cred, + oc_sec_credusage_t credusage, + oc_sec_encoded_data_t publicdata, + oc_string_view_t tag) +{ + 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)); +} + +#endif /* OC_PKI */ + int oc_sec_add_new_cred(size_t device, bool roles_resource, const oc_tls_peer_t *client, int credid, @@ -811,15 +836,12 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, break; } #ifdef OC_PKI - if (credtype == OC_CREDTYPE_CERT && cred->credusage == credusage) { - /* Trying to add a duplicate certificate chain, so ignore */ - if (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_free_string(&public_key); - return cred->credid; - } + if (credtype == OC_CREDTYPE_CERT && + /* Trying to add a duplicate certificate chain, so ignore */ + sec_match_cred_with_publicdata(cred, credusage, publicdata, + tag)) { + oc_free_string(&public_key); + return cred->credid; } #endif /* OC_PKI */ } @@ -859,7 +881,7 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, .keypair = kp, #endif /* OC_PKI */ }; - oc_sec_cred_t *cred = cred_create(create); + const oc_sec_cred_t *cred = cred_create(create); if (cred == NULL) { goto add_new_cred_error; } diff --git a/security/oc_obt_certs.c b/security/oc_obt_certs.c index 33181ae4d1..80de133eb8 100644 --- a/security/oc_obt_certs.c +++ b/security/oc_obt_certs.c @@ -24,174 +24,51 @@ #error "ERROR: Please rebuild with OC_DYNAMIC_ALLOCATION" #endif /* !OC_DYNAMIC_ALLOCATION */ -#include "api/c-timestamp/timestamp.h" -#include "oc_obt.h" #include "oc_store.h" #include "port/oc_log_internal.h" +#include "security/oc_certs_generate_internal.h" #include "security/oc_certs_internal.h" -#include "security/oc_entropy_internal.h" -#include "security/oc_keypair_internal.h" #include "security/oc_obt_internal.h" -#include "security/oc_pki_internal.h" #include "util/oc_secure_string_internal.h" -#include -#include -#include -#include - -#define OC_OBT_CERTS_SERIAL_NUMBER_SIZE 20 +#define OC_OBT_CERTS_SERIAL_NUMBER_SIZE (20) // 12/31/2029 23:59:59 #define OC_OBT_CERTS_NOT_AFTER_RFC3339 "2029-12-31T23:59:59Z" - -/* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ -static bool -oc_obt_write_validity(mbedtls_x509write_cert *ctx) -{ - timestamp_t ts = oc_certs_timestamp_now(); - char nb[15] = { 0 }; - if (!oc_certs_timestamp_format(ts, nb, sizeof(nb))) { - return false; - } - - memset(&ts, 0, sizeof(ts)); - if (timestamp_parse(OC_OBT_CERTS_NOT_AFTER_RFC3339, - sizeof(OC_OBT_CERTS_NOT_AFTER_RFC3339) - 1, &ts) != 0) { - OC_ERR("cannot parse notAfter timestamp"); - return false; - } - char na[15] = { 0 }; - if (!oc_certs_timestamp_format(ts, na, sizeof(na))) { - return false; - } - - int ret = mbedtls_x509write_crt_set_validity(ctx, nb, na); - if (ret < 0) { - OC_ERR("error writing cert validity %d", ret); - return false; - } - return true; -} +// 12/31/2029 23:59:59 to seconds since epoch +#define OC_OBT_CERTS_NOT_AFTER (1893455999) int oc_obt_generate_self_signed_root_cert_pem( oc_obt_generate_root_cert_data_t cert_data, unsigned char *buffer, size_t buffer_size) { - assert(buffer != NULL); - mbedtls_x509write_cert cert; - mbedtls_x509write_crt_init(&cert); - - mbedtls_pk_context pk; - mbedtls_pk_init(&pk); - - mbedtls_entropy_context entropy; - mbedtls_entropy_init(&entropy); - oc_entropy_add_source(&entropy); - - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ctr_drbg_init(&ctr_drbg); - #define PERSONALIZATION_DATA "IoTivity-Lite-Self-Signed-Cert" - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, - (const uint8_t *)PERSONALIZATION_DATA, - sizeof(PERSONALIZATION_DATA)); + oc_certs_generate_t self_signed_root_cert = { + .personalization_string = { (const uint8_t *)PERSONALIZATION_DATA, + sizeof(PERSONALIZATION_DATA) }, + .serial_number_size = OC_OBT_CERTS_SERIAL_NUMBER_SIZE, + /* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ + .validity = { .not_before = oc_certs_timestamp_now(), + .not_after = { OC_OBT_CERTS_NOT_AFTER, 0, 0 } }, + .subject = { + .name = cert_data.subject_name, + .public_key = { cert_data.public_key, cert_data.public_key_size }, + .private_key = { cert_data.private_key, cert_data.private_key_size }, + }, + .key_usage = { .key_usage = MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN | + MBEDTLS_X509_KU_DIGITAL_SIGNATURE }, + .signature_md = cert_data.signature_md_alg, + .is_CA = true, + }; #undef PERSONALIZATION_DATA - if (ret < 0) { - OC_ERR("error initializing RNG %d", ret); - goto exit; - } - - mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); - - ret = mbedtls_pk_parse_public_key(&pk, cert_data.public_key, - cert_data.public_key_size); - if (ret < 0) { - OC_ERR("error parsing root cert's public key %d", ret); - goto exit; - } - - ret = oc_mbedtls_pk_parse_key( - 0, &pk, cert_data.private_key, cert_data.private_key_size, - /*pwd*/ NULL, /*pwd_len*/ 0, mbedtls_ctr_drbg_random, &ctr_drbg); - if (ret < 0) { - OC_ERR("error parsing root cert's private key %d", ret); - goto exit; - } - - /* SerialNumber */ - ret = oc_certs_generate_serial_number(&cert, OC_OBT_CERTS_SERIAL_NUMBER_SIZE); - if (ret < 0) { - OC_ERR("error generating serial number for root cert"); - goto exit; - } - - /* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ - if (!oc_obt_write_validity(&cert)) { - OC_ERR("error writing root cert validity"); - goto exit; - } - - /* Version: v3 */ - mbedtls_x509write_crt_set_version(&cert, MBEDTLS_X509_CRT_VERSION_3); - /* signatureAlgorithm: ecdsa-with-SHA256 */ - mbedtls_x509write_crt_set_md_alg(&cert, cert_data.signature_md_alg); - /* Subject */ - ret = mbedtls_x509write_crt_set_subject_name(&cert, cert_data.subject_name); - if (ret < 0) { - OC_ERR("error writing root cert subject name %d", ret); - goto exit; - } - /* Issuer */ - ret = mbedtls_x509write_crt_set_issuer_name(&cert, cert_data.subject_name); - if (ret < 0) { - OC_ERR("error writing root cert issuer name %d", ret); - goto exit; - } - /* Subject Public Key Info: id-ecPublicKey, secp256r1 */ - mbedtls_x509write_crt_set_subject_key(&cert, &pk); - /* Issuer Private Key */ - mbedtls_x509write_crt_set_issuer_key(&cert, &pk); - /* basicConstraints: cA = TRUE, pathLenConstraint = unlimited */ - ret = mbedtls_x509write_crt_set_basic_constraints(&cert, 1, -1); - if (ret < 0) { - OC_ERR("error writing root cert basicConstraints %d", ret); - goto exit; - } - /* keyUsage: keyCertSign (5), cRLSign and digitalSignature(0) */ - ret = mbedtls_x509write_crt_set_key_usage( - &cert, MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN | - MBEDTLS_X509_KU_DIGITAL_SIGNATURE); - if (ret < 0) { - OC_ERR("error writing root cert keyUsage %d", ret); - goto exit; - } - - ret = mbedtls_x509write_crt_pem(&cert, buffer, buffer_size, - mbedtls_ctr_drbg_random, &ctr_drbg); - - if (ret < 0) { - OC_ERR("error serializing root cert into PEM %d", ret); - goto exit; - } - -exit: - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - mbedtls_pk_free(&pk); - mbedtls_x509write_crt_free(&cert); - if (ret < 0) { - OC_ERR("error generating self-signed root cert"); - } - return ret; + return oc_certs_generate(self_signed_root_cert, buffer, buffer_size); } int oc_obt_generate_self_signed_root_cert( oc_obt_generate_root_cert_data_t cert_data, size_t device) { - unsigned char cert_pem[4096]; if (oc_obt_generate_self_signed_root_cert_pem(cert_data, cert_pem, sizeof(cert_pem)) < 0) { @@ -222,97 +99,8 @@ oc_obt_generate_identity_cert_pem( oc_obt_generate_identity_cert_data_t cert_data, unsigned char *buffer, size_t buffer_size) { - mbedtls_x509write_cert cert; - mbedtls_x509write_crt_init(&cert); - - mbedtls_pk_context subject_pub_key; - mbedtls_pk_init(&subject_pub_key); - - mbedtls_pk_context issuer_priv_key; - mbedtls_pk_init(&issuer_priv_key); - - mbedtls_entropy_context entropy; - mbedtls_entropy_init(&entropy); - oc_entropy_add_source(&entropy); - - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ctr_drbg_init(&ctr_drbg); - #define PERSONALIZATION_DATA "IoTivity-Lite-Identity-Cert" - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, - (const uint8_t *)PERSONALIZATION_DATA, - sizeof(PERSONALIZATION_DATA)); -#undef PERSONALIZATION_DATA - if (ret < 0) { - OC_ERR("error initializing RNG %d", ret); - goto exit; - } - - mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); - - ret = mbedtls_pk_parse_public_key(&subject_pub_key, cert_data.public_key, - cert_data.public_key_size); - if (ret < 0) { - OC_ERR("error parsing subject's public key %d", ret); - goto exit; - } - - ret = - oc_mbedtls_pk_parse_key(0, &issuer_priv_key, cert_data.issuer_private_key, - cert_data.issuer_private_key_size, /*pwd*/ NULL, - /*pwdlen*/ 0, mbedtls_ctr_drbg_random, &ctr_drbg); - if (ret < 0) { - OC_ERR("error parsing issuer's private key %d", ret); - goto exit; - } - - /* SerialNumber */ - ret = oc_certs_generate_serial_number(&cert, OC_OBT_CERTS_SERIAL_NUMBER_SIZE); - if (ret < 0) { - OC_ERR("error generating serial number for identity cert"); - goto exit; - } - - /* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ - if (!oc_obt_write_validity(&cert)) { - OC_ERR("error writing identity cert validity"); - goto exit; - } - - /* Version: v3 */ - mbedtls_x509write_crt_set_version(&cert, MBEDTLS_X509_CRT_VERSION_3); - /* signatureAlgorithm: ecdsa-with-SHA256 */ - mbedtls_x509write_crt_set_md_alg(&cert, cert_data.signature_md_alg); - /* Subject */ - ret = mbedtls_x509write_crt_set_subject_name(&cert, cert_data.subject_name); - if (ret < 0) { - OC_ERR("error writing identity cert subject name %d", ret); - goto exit; - } - /* Issuer */ - ret = mbedtls_x509write_crt_set_issuer_name(&cert, cert_data.issuer_name); - if (ret < 0) { - OC_ERR("error writing identity cert issuer name %d", ret); - goto exit; - } - /* Subject Public Key Info: id-ecPublicKey, secp256r1 */ - mbedtls_x509write_crt_set_subject_key(&cert, &subject_pub_key); - /* Issuer Private Key */ - mbedtls_x509write_crt_set_issuer_key(&cert, &issuer_priv_key); - /* basicConstraints: cA = FALSE, pathLenConstraint = not present */ - ret = mbedtls_x509write_crt_set_basic_constraints(&cert, 0, 0); - if (ret < 0) { - OC_ERR("error writing identity cert basicConstraints %d", ret); - goto exit; - } - /* keyUsage: digitalSignature (0) and keyAgreement(4) */ - ret = mbedtls_x509write_crt_set_key_usage( - &cert, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT); - if (ret < 0) { - OC_ERR("error writing identity cert keyUsage %d", ret); - goto exit; - } - /* extendedKeyUsage: serverAuthentication , clientAuthentication, Identity + /* extendedKeyUsage: serverAuthentication, clientAuthentication, Identity * certificate */ const unsigned char extendedKeyUsage[] = { MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED, /* SEQUENCE OF.. Tag */ @@ -351,240 +139,46 @@ oc_obt_generate_identity_cert_pem( 0x06 /* OID: 1.3.6.1.4.1.44924.1.6 */ }; - ret = mbedtls_x509write_crt_set_extension( - &cert, MBEDTLS_OID_EXTENDED_KEY_USAGE, - MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), 0, extendedKeyUsage, - sizeof(extendedKeyUsage)); - if (ret < 0) { - OC_ERR("could not write extendedKeyUsage into cert %d", ret); - goto exit; - } - - ret = mbedtls_x509write_crt_pem(&cert, buffer, buffer_size, - mbedtls_ctr_drbg_random, &ctr_drbg); - - if (ret < 0) { - OC_ERR("error serializing identity cert into PEM %d", ret); - goto exit; - } - -exit: - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - mbedtls_pk_free(&subject_pub_key); - mbedtls_pk_free(&issuer_priv_key); - mbedtls_x509write_crt_free(&cert); - if (ret < 0) { - OC_ERR("error generating identity cert"); - } - return ret; -} - -void -oc_obt_free_encoded_roles(mbedtls_x509_general_names *general_names) -{ - while (general_names != NULL) { - mbedtls_x509_general_names *name = general_names->next; - mbedtls_asn1_free_named_data_list( - &general_names->general_name.name.directory_name); - free(general_names); - general_names = name; - } -} - -static mbedtls_x509_general_names * -oc_obt_encode_role(const oc_role_t *role) -{ - char roleid[512]; - if (!oc_certs_encode_role(role, roleid, sizeof(roleid))) { - OC_ERR("error encoding roleid"); - return NULL; - } - /* A RoleId is encoded in a GeneralName that is of type directoryName into - * the GeneralNames SEQUEENCE. - */ - mbedtls_x509_general_names *name = - (mbedtls_x509_general_names *)calloc(1, sizeof(mbedtls_x509_general_names)); - if (name == NULL) { - OC_ERR("error allocating memory for GeneralName"); - return NULL; - } - name->general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME; - - int ret = mbedtls_x509_string_to_names( - &name->general_name.name.directory_name, roleid); - if (ret < 0) { - OC_ERR("error writing roleid to GeneralName %d", ret); - mbedtls_asn1_free_named_data_list(&name->general_name.name.directory_name); - free(name); - return NULL; - } - - return name; -} - -int -oc_obt_encode_roles(const oc_role_t *roles, - mbedtls_x509_general_names **general_names) -{ - mbedtls_x509_general_names *names = NULL; - - int count = 0; - while (roles != NULL) { - mbedtls_x509_general_names *name = oc_obt_encode_role(roles); - if (name == NULL) { - oc_obt_free_encoded_roles(names); - return -1; - } - - if (names != NULL) { - names->next = name; - } else { - names = name; - } - - ++count; - roles = roles->next; - } - - if (general_names != NULL) { - *general_names = names; - } - return count; -} - -static bool -oc_obt_write_roles_to_subject_alt_names(mbedtls_x509write_cert *cert, - const oc_role_t *roles) -{ - mbedtls_x509_general_names *general_names = NULL; - int ret = oc_obt_encode_roles(roles, &general_names); - if (ret < 0) { - return false; - } + oc_certs_key_usage_t key_usage = { + .key_usage = + MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT, + .extended_key_usage = { extendedKeyUsage, sizeof(extendedKeyUsage) }, + }; - ret = mbedtls_x509write_crt_set_subject_alt_names(cert, general_names); - if (ret < 0) { - OC_ERR("error writing subjectAlternativeName to cert %d", ret); - oc_obt_free_encoded_roles(general_names); - return false; - } + oc_certs_generate_t identity_cert = { + .personalization_string = { (const uint8_t *)PERSONALIZATION_DATA, + sizeof(PERSONALIZATION_DATA) }, + .serial_number_size = OC_OBT_CERTS_SERIAL_NUMBER_SIZE, + /* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ + .validity = { .not_before = oc_certs_timestamp_now(), + .not_after = { OC_OBT_CERTS_NOT_AFTER, 0, 0 } }, + .subject = { .name = cert_data.subject_name, + .public_key = { cert_data.public_key, + cert_data.public_key_size } }, + .issuer = { .name = cert_data.issuer_name, + .private_key = { cert_data.issuer_private_key, + cert_data.issuer_private_key_size } }, + .key_usage = key_usage, + .signature_md = cert_data.signature_md_alg, + }; +#undef PERSONALIZATION_DATA - oc_obt_free_encoded_roles(general_names); - return true; + return oc_certs_generate(identity_cert, buffer, buffer_size); } int oc_obt_generate_role_cert_pem(oc_obt_generate_role_cert_data_t cert_data, unsigned char *buffer, size_t buffer_size) + { - assert(buffer != NULL); if (cert_data.roles == NULL) { OC_ERR("did not provide roles"); return -1; } - mbedtls_x509write_cert cert; - mbedtls_x509write_crt_init(&cert); - - mbedtls_pk_context subject_pub_key; - mbedtls_pk_init(&subject_pub_key); - - mbedtls_pk_context issuer_priv_key; - mbedtls_pk_init(&issuer_priv_key); - - mbedtls_entropy_context entropy; - mbedtls_entropy_init(&entropy); - oc_entropy_add_source(&entropy); - - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ctr_drbg_init(&ctr_drbg); - #define PERSONALIZATION_DATA "IoTivity-Lite-Role-Cert" - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, - (const uint8_t *)PERSONALIZATION_DATA, - sizeof(PERSONALIZATION_DATA)); -#undef PERSONALIZATION_DATA - if (ret < 0) { - OC_ERR("error initializing RNG %d", ret); - goto exit; - } - - mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); - - ret = mbedtls_pk_parse_public_key(&subject_pub_key, cert_data.public_key, - cert_data.public_key_size); - if (ret < 0) { - OC_ERR("error parsing subject's public key %d", ret); - goto exit; - } - - ret = - oc_mbedtls_pk_parse_key(0, &issuer_priv_key, cert_data.issuer_private_key, - cert_data.issuer_private_key_size, 0, 0, - mbedtls_ctr_drbg_random, &ctr_drbg); - if (ret < 0) { - OC_ERR("error parsing issuer's private key %d", ret); - goto exit; - } - - /* SerialNumber */ - ret = oc_certs_generate_serial_number(&cert, OC_OBT_CERTS_SERIAL_NUMBER_SIZE); - if (ret < 0) { - OC_ERR("error generating serial number for role cert"); - goto exit; - } - - /* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ - if (!oc_obt_write_validity(&cert)) { - OC_ERR("error writing role cert validity"); - goto exit; - } - /* Version: v3 */ - mbedtls_x509write_crt_set_version(&cert, MBEDTLS_X509_CRT_VERSION_3); - /* signatureAlgorithm: ecdsa-with-SHA256 */ - mbedtls_x509write_crt_set_md_alg(&cert, cert_data.signature_md_alg); - /* Subject */ - ret = mbedtls_x509write_crt_set_subject_name(&cert, cert_data.subject_name); - if (ret < 0) { - OC_ERR("error writing role cert subject name %d", ret); - goto exit; - } - /* Issuer */ - ret = mbedtls_x509write_crt_set_issuer_name(&cert, cert_data.issuer_name); - if (ret < 0) { - OC_ERR("error writing role cert issuer name %d", ret); - goto exit; - } - /* Subject Public Key Info: id-ecPublicKey, secp256r1 */ - mbedtls_x509write_crt_set_subject_key(&cert, &subject_pub_key); - /* Issuer Private Key */ - mbedtls_x509write_crt_set_issuer_key(&cert, &issuer_priv_key); - /* basicConstraints: cA = FALSE, pathLenConstraint = not present */ - ret = mbedtls_x509write_crt_set_basic_constraints(&cert, 0, 0); - if (ret < 0) { - OC_ERR("error writing role cert basicConstraints %d", ret); - goto exit; - } - /* keyUsage: digitalSignature (0) and keyAgreement(4) */ - ret = mbedtls_x509write_crt_set_key_usage( - &cert, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT); - if (ret < 0) { - OC_ERR("error writing role cert keyUsage %d", ret); - goto exit; - } - - /* The subjectAlternativeName extension is populated with the GeneralNames - * SEQUENCE containing the Role. - */ - if (!oc_obt_write_roles_to_subject_alt_names(&cert, cert_data.roles)) { - OC_ERR("error writing role cert subject alt names"); - ret = -1; - goto exit; - } - - /* extendedKeyUsage: serverAuthentication , clientAuthentication, Role + /* extendedKeyUsage: serverAuthentication, clientAuthentication, Role * certificate */ const unsigned char extendedKeyUsage[] = { MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED, /* SEQUENCE OF.. Tag */ @@ -623,31 +217,33 @@ oc_obt_generate_role_cert_pem(oc_obt_generate_role_cert_data_t cert_data, 0x07 /* OID: 1.3.6.1.4.1.44924.1.7 */ }; - ret = mbedtls_x509write_crt_set_extension( - &cert, MBEDTLS_OID_EXTENDED_KEY_USAGE, - MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), 0, extendedKeyUsage, - sizeof(extendedKeyUsage)); - if (ret < 0) { - OC_ERR("error writing extendedKeyUsage to cert %d", ret); - goto exit; - } + oc_certs_key_usage_t key_usage = { + /* keyUsage: digitalSignature (0) and keyAgreement(4) */ + .key_usage = + MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT, + .extended_key_usage = { extendedKeyUsage, sizeof(extendedKeyUsage) }, + }; - ret = mbedtls_x509write_crt_pem(&cert, buffer, buffer_size, - mbedtls_ctr_drbg_random, &ctr_drbg); - if (ret < 0) { - OC_ERR("error serializing role cert into PEM"); - } -exit: - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - mbedtls_pk_free(&subject_pub_key); - mbedtls_pk_free(&issuer_priv_key); - mbedtls_x509write_crt_free(&cert); - if (ret < 0) { - OC_ERR("error generating role cert"); - return -1; - } - return 0; + oc_certs_generate_t identity_cert = { + .personalization_string = { (const uint8_t *)PERSONALIZATION_DATA, + sizeof(PERSONALIZATION_DATA) }, + .serial_number_size = OC_OBT_CERTS_SERIAL_NUMBER_SIZE, + /* notBefore and notAfter: [now] to 12/31/2029 23:59:59 */ + .validity = { .not_before = oc_certs_timestamp_now(), + .not_after = { OC_OBT_CERTS_NOT_AFTER, 0, 0 } }, + .subject = { .name = cert_data.subject_name, + .public_key = { cert_data.public_key, + cert_data.public_key_size } }, + .issuer = { .name = cert_data.issuer_name, + .private_key = { cert_data.issuer_private_key, + cert_data.issuer_private_key_size } }, + .key_usage = key_usage, + .signature_md = cert_data.signature_md_alg, + .roles = cert_data.roles, + }; +#undef PERSONALIZATION_DATA + + return oc_certs_generate(identity_cert, buffer, buffer_size); } #endif /* OC_SECURITY && OC_PKI */ diff --git a/security/oc_obt_internal.h b/security/oc_obt_internal.h index d27dbf0a9f..962d0aa92b 100644 --- a/security/oc_obt_internal.h +++ b/security/oc_obt_internal.h @@ -29,6 +29,7 @@ #include "oc_uuid.h" #include "security/oc_pstat.h" #include "util/oc_list.h" +#include "util/oc_compiler.h" #ifdef OC_PKI #include @@ -272,7 +273,7 @@ typedef struct oc_obt_generate_root_cert_data_t */ int oc_obt_generate_self_signed_root_cert_pem( oc_obt_generate_root_cert_data_t cert_data, unsigned char *buffer, - size_t buffer_size); + size_t buffer_size) OC_NONNULL(); /** * @brief Generate a self-signed certificate and add it to credentials of given @@ -309,24 +310,7 @@ typedef struct oc_obt_generate_identity_cert_data_t */ int oc_obt_generate_identity_cert_pem( oc_obt_generate_identity_cert_data_t cert_data, unsigned char *buffer, - size_t buffer_size); - -/** - * @brief Encode linked list of role and authority pairs into linked list of - * mbedtls_x509_general_names* - * - * @param[in] roles linked list of role-authority pairs - * @param[out] general_names output pointer to store linked list of - * mbedtls_x509_general_names * (cannot be NULL, must be deallocated by - * oc_obt_free_encoded_roles) - * @return >=0 on success, number of encoded roles - * @return -1 on error - */ -int oc_obt_encode_roles(const oc_role_t *roles, - mbedtls_x509_general_names **general_names); - -/// @brief Deallocate a linked list of mbedtls_x509_general_names* -void oc_obt_free_encoded_roles(mbedtls_x509_general_names *general_names); + size_t buffer_size) OC_NONNULL(); typedef struct oc_obt_generate_role_cert_data_t { @@ -351,7 +335,8 @@ typedef struct oc_obt_generate_role_cert_data_t * @return -1 on error */ int oc_obt_generate_role_cert_pem(oc_obt_generate_role_cert_data_t cert_data, - unsigned char *buffer, size_t buffer_size); + unsigned char *buffer, size_t buffer_size) + OC_NONNULL(); #endif /* OC_PKI */ diff --git a/security/oc_roles.c b/security/oc_roles.c index 97cdb928a6..2ff73621a0 100644 --- a/security/oc_roles.c +++ b/security/oc_roles.c @@ -203,86 +203,87 @@ oc_sec_roles_get(const oc_tls_peer_t *client) } static void -free_cred_properties(oc_sec_cred_t *cred) +sec_free_role(oc_sec_cred_t *cred) { + mbedtls_x509_crt_free(cred->ctx); + oc_memb_free(&g_x509_crt_s, cred->ctx); oc_free_string(&cred->role.role); oc_free_string(&cred->role.authority); oc_free_string(&cred->publicdata.data); + oc_memb_free(&g_roles_s, cred); } -void +bool oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client) { oc_sec_roles_t *roles = roles_get_for_client(client); if (roles == NULL) { - return; + return false; } oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); while (r != NULL) { if (role == r) { oc_list_remove(roles->roles, r); - mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&g_x509_crt_s, r->ctx); - free_cred_properties(r); - oc_memb_free(&g_roles_s, r); - return; + sec_free_role(r); + return true; } r = r->next; } + return false; } -void +int oc_sec_free_roles_for_device(size_t device) { + int removed = 0; oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(g_clients); - while (roles) { + while (roles != NULL) { oc_sec_roles_t *next = roles->next; if (roles->device == device) { - oc_sec_free_roles(roles->client); + removed += oc_sec_free_roles(roles->client); } roles = next; } + return removed; } -void +int oc_sec_free_roles(const oc_tls_peer_t *client) { oc_sec_roles_t *roles = roles_get_for_client(client); if (roles == NULL) { - return; + return 0; } + + int removed = 0; oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_pop(roles->roles); - while (r) { - mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&g_x509_crt_s, r->ctx); - free_cred_properties(r); - oc_memb_free(&g_roles_s, r); + while (r != NULL) { + sec_free_role(r); + ++removed; r = (oc_sec_cred_t *)oc_list_pop(roles->roles); } oc_list_remove(g_clients, roles); oc_memb_free(&g_clients_s, roles); + return removed; } -int +bool oc_sec_free_role_by_credid(int credid, const oc_tls_peer_t *client) { oc_sec_roles_t *roles = roles_get_for_client(client); if (roles == NULL) { - return -1; + return false; } oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); - while (r) { + while (r != NULL) { if (r->credid == credid) { oc_list_remove(roles->roles, r); - mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&g_x509_crt_s, r->ctx); - free_cred_properties(r); - oc_memb_free(&g_roles_s, r); - return 0; + sec_free_role(r); + return true; } r = r->next; } - return -1; + return false; } static void @@ -372,18 +373,21 @@ roles_resource_delete(oc_request_t *request, oc_interface_mask_t iface_mask, const oc_tls_peer_t *client = oc_tls_get_peer(request->origin); const char *query_param = NULL; int ret = oc_get_query_value(request, "credid", &query_param); - if (ret != -1) { - errno = 0; - long credid = - strtol(query_param, NULL, 10); // NOLINT(readability-magic-numbers) - if (errno != 0 || credid > INT32_MAX || credid < 0 || - oc_sec_free_role_by_credid((int)credid, client) < 0) { - OC_ERR("cannot delete roles credential: invalid credid(%ld)", credid); - oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); - return; - } - } else { + if (ret == -1) { + // no query param, delete all roles oc_sec_free_roles(client); + oc_send_response_with_callback(request, OC_STATUS_DELETED, true); + return; + } + + errno = 0; + long credid = + strtol(query_param, NULL, 10); // NOLINT(readability-magic-numbers) + if (errno != 0 || credid > INT32_MAX || credid < 0 || + !oc_sec_free_role_by_credid((int)credid, client)) { + OC_ERR("cannot delete roles credential: invalid credid(%ld)", credid); + oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); + return; } oc_send_response_with_callback(request, OC_STATUS_DELETED, true); } diff --git a/security/oc_roles_internal.h b/security/oc_roles_internal.h index dad981969a..7a48a21664 100644 --- a/security/oc_roles_internal.h +++ b/security/oc_roles_internal.h @@ -73,16 +73,53 @@ oc_sec_cred_t *oc_sec_roles_add(const oc_tls_peer_t *client, size_t device) */ void oc_sec_roles_create_resource(size_t device); -void oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client); -void oc_sec_free_roles(const oc_tls_peer_t *client); -void oc_sec_free_roles_for_device(size_t device); -int oc_sec_free_role_by_credid(int credid, const oc_tls_peer_t *client); +/** + * @brief Remove role from the list of roles for given client and deallocate it. + * + * @param role role to remove (cannot be NULL) + * @param client client asserting the role (cannot be NULL) + * @return true on success + * @return false on failure + */ +bool oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client) + OC_NONNULL(); + +/** + * @brief Remove all roles asserted by given client and deallocate them. + * + * @param client client asserting the role (cannot be NULL) + * @return number of roles removed + */ +int oc_sec_free_roles(const oc_tls_peer_t *client) OC_NONNULL(); + +/** + * @brief Remove all roles asserted by given client for given device and + * deallocate them. + * + * @param device device index + * @return number of roles removed + */ +int oc_sec_free_roles_for_device(size_t device); + +/** + * @brief Remove role with given credid asserted by given client for given + * device and deallocate them. + * + * @param credid credid of the role to remove + * @param client client asserting the role (cannot be NULL) + * @return true on success + * @return false on failure + */ +bool oc_sec_free_role_by_credid(int credid, const oc_tls_peer_t *client) + OC_NONNULL(); /** @} */ #ifdef OC_CLIENT -#define OC_ROLES_NUM_ROLE_CREDS (2) +enum { + OC_ROLES_NUM_ROLE_CREDS = 2, +}; /** * \defgroup client-roles Event timers diff --git a/security/unittest/certstest.cpp b/security/unittest/certstest.cpp index 5046ab542c..618d2a2624 100644 --- a/security/unittest/certstest.cpp +++ b/security/unittest/certstest.cpp @@ -21,6 +21,7 @@ #include "oc_certs.h" #include "port/oc_log_internal.h" #include "port/oc_random.h" +#include "security/oc_certs_generate_internal.h" #include "security/oc_certs_internal.h" #include @@ -64,6 +65,8 @@ TEST_F(TestCerts, IsPem) EXPECT_TRUE(is_pem("-----BEGIN CERTIFICATE-----")); } +#ifdef OC_DYNAMIC_ALLOCATION + TEST_F(TestCerts, TimestampFormatFail) { timestamp_t ts = oc_certs_timestamp_now(); @@ -87,6 +90,8 @@ TEST_F(TestCerts, TimestampFormat) OC_DBG("notAfter: %s", buffer.data()); } +#endif /* OC_DYNAMIC_ALLOCATION */ + TEST_F(TestCerts, SetSignatureMDAlgorithm) { std::vector all_mds{ diff --git a/security/unittest/obt_certstest.cpp b/security/unittest/obt_certstest.cpp index 24f54837a1..4a81e2cfac 100644 --- a/security/unittest/obt_certstest.cpp +++ b/security/unittest/obt_certstest.cpp @@ -86,6 +86,7 @@ class TestObtCerts : public testing::Test { return std::string(container.begin(), container.end()); } + static std::vector GetPEM(std::vector &data); std::vector GenerateSelfSignedRootCertificate( oc::keypair_t &kp, mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256) const; std::vector GenerateIdentityCertificate( @@ -103,6 +104,20 @@ static const std::string g_root_subject_name{ "IoTivity-Lite Test" }; static const std::string g_root_subject{ "C=US, O=OCF, CN=" + g_root_subject_name }; +std::vector +TestObtCerts::GetPEM(std::vector &data) +{ + auto it = + std::find(data.begin(), data.end(), static_cast('\0')); + size_t data_len = + std::distance(data.begin(), it) + 1; // size with NULL terminator + EXPECT_NE(data.end(), it); + + EXPECT_TRUE(oc_certs_is_PEM(&data[0], data_len)); + data.resize(data_len); + return data; +} + std::vector TestObtCerts::GenerateSelfSignedRootCertificate(oc::keypair_t &kp, mbedtls_md_type_t sig_alg) const @@ -122,15 +137,7 @@ TestObtCerts::GenerateSelfSignedRootCertificate(oc::keypair_t &kp, cert_data, cert_buf.data(), cert_buf.size()); EXPECT_EQ(0, err); - auto it = std::find(cert_buf.begin(), cert_buf.end(), - static_cast('\0')); - size_t cert_buf_len = - std::distance(cert_buf.begin(), it) + 1; // size with NULL terminator - EXPECT_NE(cert_buf.end(), it); - - EXPECT_TRUE(oc_certs_is_PEM(&cert_buf[0], cert_buf_len)); - cert_buf.resize(cert_buf_len); - return cert_buf; + return GetPEM(cert_buf); } std::vector @@ -152,17 +159,7 @@ TestObtCerts::GenerateIdentityCertificate(oc::keypair_t &kp, int err = oc_obt_generate_identity_cert_pem(cert_data, cert_buf.data(), cert_buf.size()); EXPECT_EQ(0, err); - - auto it = std::find(cert_buf.begin(), cert_buf.end(), - static_cast('\0')); - size_t cert_buf_len = - std::distance(cert_buf.begin(), it) + 1; // size with NULL terminator - EXPECT_NE(cert_buf.end(), it); - std::string pem(cert_buf.begin(), it); - - EXPECT_TRUE(oc_certs_is_PEM(&cert_buf[0], cert_buf_len)); - cert_buf.resize(cert_buf_len); - return cert_buf; + return GetPEM(cert_buf); } std::vector @@ -185,17 +182,7 @@ TestObtCerts::GenerateRoleCertificate(oc::keypair_t &kp, int err = oc_obt_generate_role_cert_pem(cert_data, cert_buf.data(), cert_buf.size()); EXPECT_EQ(0, err); - - auto it = std::find(cert_buf.begin(), cert_buf.end(), - static_cast('\0')); - size_t cert_buf_len = - std::distance(cert_buf.begin(), it) + 1; // size with NULL terminator - EXPECT_NE(cert_buf.end(), it); - std::string pem(cert_buf.begin(), it); - - EXPECT_TRUE(oc_certs_is_PEM(&cert_buf[0], cert_buf_len)); - cert_buf.resize(cert_buf_len); - return cert_buf; + return GetPEM(cert_buf); } TEST_F(TestObtCerts, GenerateSelfSignedRootCertificateFail) diff --git a/security/unittest/rolestest.cpp b/security/unittest/rolestest.cpp index 00b2b3f65c..684fb0bfdd 100644 --- a/security/unittest/rolestest.cpp +++ b/security/unittest/rolestest.cpp @@ -18,12 +18,27 @@ #if defined(OC_SECURITY) && defined(OC_PKI) +#include "api/oc_ri_internal.h" #include "oc_core_res.h" +#include "port/oc_log_internal.h" +#include "security/oc_certs_generate_internal.h" +#include "security/oc_certs_internal.h" +#include "security/oc_obt_internal.h" #include "security/oc_roles_internal.h" +#include "security/oc_security_internal.h" +#include "security/oc_tls_internal.h" #include "tests/gtest/Device.h" +#include "tests/gtest/Endpoint.h" +#include "tests/gtest/KeyPair.h" +#include "tests/gtest/RepPool.h" #include "tests/gtest/Resource.h" +#include "tests/gtest/Role.h" +#include "util/oc_secure_string_internal.h" +#include +#include #include +#include static constexpr size_t kDeviceID{ 0 }; @@ -31,20 +46,220 @@ class TestRolesWithServer : public testing::Test { public: static void SetUpTestCase() { + // TOOD: rm + oc_log_set_level(OC_LOG_LEVEL_DEBUG); + ASSERT_TRUE(oc::TestDevice::StartServer()); #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM ASSERT_TRUE( oc::SetAccessInRFOTM(OCF_SEC_ROLES, kDeviceID, true, OC_PERM_RETRIEVE | OC_PERM_UPDATE | OC_PERM_DELETE)); #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + + g_uuid = oc_core_get_device_id(kDeviceID); + ASSERT_NE(nullptr, g_uuid); +#ifdef OC_DYNAMIC_ALLOCATION + g_root_keypair = oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1); + g_root_credid = generateSelfSignedRootCertificate( + g_root_keypair, g_root_subject, MBEDTLS_MD_SHA256); + ASSERT_GT(g_root_credid, 0); +#endif /* OC_DYNAMIC_ALLOCATION */ } static void TearDownTestCase() { oc::TestDevice::StopServer(); } + + void TearDown() override + { + for (auto &peer : peers_) { + oc_tls_remove_peer(&peer->endpoint); + } + peers_.clear(); + } + + oc_tls_peer_t *addPeer(const oc_endpoint_t *ep); + +#ifdef OC_DYNAMIC_ALLOCATION + static int generateSelfSignedRootCertificate(const oc::keypair_t &kp, + const std::string &subject_name, + mbedtls_md_type_t sig_alg); + + static std::vector generateRoleCertificatePEM( + const oc::keypair_t &kp, const oc::Roles &roles, + const std::string &subject_name, const std::string &issuer_name, + mbedtls_md_type_t sig_alg); + + static bool addRolesByCertificate(const oc_uuid_t *uuid, + const oc::keypair_t &kp, + const oc::Roles &roles, + const std::string &subject_name, + const oc_endpoint_t *ep); + + static size_t countRoles(const oc_tls_peer_t *peer); +#endif /* OC_DYNAMIC_ALLOCATION */ + + std::vector peers_{}; + + static oc_uuid_t *g_uuid; + static std::string g_root_subject_name; + static std::string g_root_subject; + static oc::keypair_t g_root_keypair; + static int g_root_credid; }; +std::string TestRolesWithServer::g_root_subject_name{ "IoTivity-Lite Test" }; +std::string TestRolesWithServer::g_root_subject{ "C=US, O=OCF, CN=" + + g_root_subject_name }; +oc::keypair_t TestRolesWithServer::g_root_keypair{}; +int TestRolesWithServer::g_root_credid{ -1 }; +oc_uuid_t *TestRolesWithServer::g_uuid{}; + +oc_tls_peer_t * +TestRolesWithServer::addPeer(const oc_endpoint_t *ep) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + if (pstat == nullptr) { + return nullptr; + } + pstat->s = OC_DOS_RFNOP; + oc_tls_peer_t *peer = + oc_tls_add_or_get_peer(ep, MBEDTLS_SSL_IS_SERVER, nullptr); + pstat->s = OC_DOS_RFOTM; + if (peer == nullptr) { + return nullptr; + } + memcpy(peer->uuid.id, g_uuid->id, sizeof(g_uuid->id)); + peers_.push_back(peer); + return peer; +} + +#ifdef OC_DYNAMIC_ALLOCATION + +static std::vector +getPEM(std::vector &data) +{ + auto it = + std::find(data.begin(), data.end(), static_cast('\0')); + size_t data_len = + std::distance(data.begin(), it) + 1; // size with NULL terminator + EXPECT_NE(data.end(), it); + + EXPECT_TRUE(oc_certs_is_PEM(&data[0], data_len)); + data.resize(data_len); + return data; +} + +int +TestRolesWithServer::generateSelfSignedRootCertificate( + const oc::keypair_t &kp, const std::string &subject_name, + mbedtls_md_type_t sig_alg) +{ + oc_obt_generate_root_cert_data_t cert_data = { + /*.subject_name = */ subject_name.c_str(), + /*.public_key =*/kp.public_key.data(), + /*.public_key_size =*/kp.public_key_size, + /*.private_key =*/kp.private_key.data(), + /*.private_key_size =*/kp.private_key_size, + /*.signature_md_alg=*/sig_alg, + }; + + return oc_obt_generate_self_signed_root_cert(cert_data, kDeviceID); +} + +std::vector +TestRolesWithServer::generateRoleCertificatePEM(const oc::keypair_t &kp, + const oc::Roles &roles, + const std::string &subject_name, + const std::string &issuer_name, + mbedtls_md_type_t sig_alg) +{ + oc_obt_generate_role_cert_data_t cert_data = { + /*.roles =*/roles.Head(), + /*.subject_name =*/subject_name.c_str(), + /*.public_key =*/kp.public_key.data(), + /*.public_key_size =*/kp.public_key_size, + /*.issuer_name =*/issuer_name.c_str(), + /*.issuer_private_key =*/kp.private_key.data(), + /*.issuer_private_key_size =*/kp.private_key_size, + /*.signature_md_alg=*/sig_alg, + }; + + std::vector cert_buf{}; + cert_buf.resize(4096, '\0'); + EXPECT_EQ(0, oc_obt_generate_role_cert_pem(cert_data, cert_buf.data(), + cert_buf.size())); + return getPEM(cert_buf); +} + +bool +TestRolesWithServer::addRolesByCertificate(const oc_uuid_t *uuid, + const oc::keypair_t &kp, + const oc::Roles &roles, + const std::string &subject_name, + const oc_endpoint_t *ep) +{ + std::array uuid_buf{}; + if (!oc_certs_encode_CN_with_UUID(uuid, uuid_buf.data(), uuid_buf.size())) { + return false; + } + auto role_pem = generateRoleCertificatePEM(kp, roles, uuid_buf.data(), + subject_name, MBEDTLS_MD_SHA256); + if (role_pem.empty()) { + return false; + } + + const oc_resource_t *roles_resource = + oc_core_get_resource_by_index(OCF_SEC_ROLES, kDeviceID); + if (roles_resource == nullptr) { + return false; + } + + oc::RepPool pool{}; + oc_rep_start_root_object(); + oc_rep_set_array(root, creds); + oc_rep_object_array_start_item(creds); + + oc_rep_set_int(creds, credtype, OC_CREDTYPE_CERT); + std::string subjectuuid{ "*" }; + oc_rep_set_text_string_v1(creds, subjectuuid, subjectuuid.c_str(), + subjectuuid.length()); + + oc_rep_set_object(creds, publicdata); + oc_rep_set_text_string_v1(publicdata, data, + reinterpret_cast(&role_pem[0]), + role_pem.size() - 1); + + std::string encoding{ OC_ENCODING_PEM_STR }; + oc_rep_set_text_string_v1(publicdata, encoding, encoding.c_str(), + encoding.length()); + oc_rep_close_object(creds, publicdata); + std::string credusage{ OC_CREDUSAGE_ROLE_CERT_STR }; + oc_rep_set_text_string_v1(creds, credusage, credusage.c_str(), + credusage.length()); + oc_rep_object_array_end_item(creds); + oc_rep_close_array(root, creds); + oc_rep_end_root_object(); + + return oc_sec_apply_cred(pool.ParsePayload().get(), roles_resource, ep, + nullptr, nullptr) == 0; +} + +size_t +TestRolesWithServer::countRoles(const oc_tls_peer_t *peer) +{ + size_t count = 0; + auto roles = oc_sec_roles_get(peer); + while (roles != nullptr) { + ++count; + roles = roles->next; + } + return count; +} + +#endif /* OC_DYNAMIC_ALLOCATION */ + TEST_F(TestRolesWithServer, GetResourceByIndex) { EXPECT_NE(nullptr, oc_core_get_resource_by_index(OCF_SEC_ROLES, kDeviceID)); @@ -55,14 +270,287 @@ TEST_F(TestRolesWithServer, GetResourceByURI) EXPECT_NE(nullptr, oc_core_get_resource_by_uri(OCF_SEC_ROLES_URI, kDeviceID)); } +#ifdef OC_DYNAMIC_ALLOCATION + +TEST_F(TestRolesWithServer, AddRole) +{ + oc::Roles roles{}; + roles.Add("user1", "role1"); + roles.Add("user1", "role2"); + roles.Add("user2", "role3"); + + oc_endpoint_t ep = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer = addPeer(&ep); + ASSERT_NE(nullptr, peer); + + EXPECT_TRUE( + addRolesByCertificate(g_uuid, g_root_keypair, roles, g_root_subject, &ep)); + EXPECT_EQ(1, countRoles(peer)); +} + +// using roles with "oic.role" prefix is prohibited, except for those defined in +// g_allowed_roles array +TEST_F(TestRolesWithServer, AddRole_FailAssertion) +{ + oc::Roles roles{}; + roles.Add("oic.role.fail"); + + oc_endpoint_t ep = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer = addPeer(&ep); + ASSERT_NE(nullptr, peer); + + EXPECT_FALSE( + addRolesByCertificate(g_uuid, g_root_keypair, roles, g_root_subject, &ep)); + EXPECT_EQ(0, countRoles(peer)); +} + +TEST_F(TestRolesWithServer, AddRole_AssertAllowed) +{ + oc::Roles roles{}; + roles.Add("oic.role.owner"); + + oc_endpoint_t ep = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer = addPeer(&ep); + ASSERT_NE(nullptr, peer); + + EXPECT_TRUE( + addRolesByCertificate(g_uuid, g_root_keypair, roles, g_root_subject, &ep)); + EXPECT_EQ(1, countRoles(peer)); +} + +TEST_F(TestRolesWithServer, FreeRole) +{ + oc_endpoint_t ep1 = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer1 = addPeer(&ep1); + ASSERT_NE(nullptr, peer1); + oc_sec_cred_t *role1 = oc_sec_roles_add(peer1, kDeviceID); + ASSERT_NE(nullptr, role1); + oc_sec_cred_t *role2 = oc_sec_roles_add(peer1, kDeviceID); + ASSERT_NE(nullptr, role2); + + oc_endpoint_t ep2 = oc::endpoint::FromString("coaps://[::2]:42"); + const auto *peer2 = addPeer(&ep2); + ASSERT_NE(nullptr, peer2); + oc_sec_cred_t *role3 = oc_sec_roles_add(peer2, kDeviceID); + ASSERT_NE(nullptr, role3); + + oc_endpoint_t ep3 = oc::endpoint::FromString("coaps://[::3]:42"); + const auto *peer3 = addPeer(&ep3); + ASSERT_NE(nullptr, peer3); + + EXPECT_FALSE(oc_sec_free_role(role1, peer3)); + EXPECT_FALSE(oc_sec_free_role(role1, peer2)); + EXPECT_EQ(1, countRoles(peer2)); + + EXPECT_EQ(2, countRoles(peer1)); + EXPECT_TRUE(oc_sec_free_role(role1, peer1)); + EXPECT_EQ(1, countRoles(peer1)); + EXPECT_TRUE(oc_sec_free_role(role2, peer1)); + EXPECT_EQ(0, countRoles(peer1)); +} + +TEST_F(TestRolesWithServer, FreeRoles) +{ + size_t deviceID1 = 1; + oc_endpoint_t ep1 = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer1 = addPeer(&ep1); + ASSERT_NE(nullptr, peer1); + oc_sec_cred_t *role1 = oc_sec_roles_add(peer1, deviceID1); + ASSERT_NE(nullptr, role1); + oc_sec_cred_t *role2 = oc_sec_roles_add(peer1, deviceID1); + ASSERT_NE(nullptr, role2); + size_t deviceID2 = 2; + oc_sec_cred_t *role3 = oc_sec_roles_add(peer1, deviceID2); + ASSERT_NE(nullptr, role3); + + oc_endpoint_t ep2 = oc::endpoint::FromString("coaps://[::2]:42"); + const auto *peer2 = addPeer(&ep2); + ASSERT_NE(nullptr, peer2); + EXPECT_EQ(0, oc_sec_free_roles(peer2)); + + EXPECT_EQ(3, countRoles(peer1)); + EXPECT_EQ(3, oc_sec_free_roles(peer1)); + EXPECT_EQ(0, countRoles(peer1)); +} + +TEST_F(TestRolesWithServer, FreeRoleForDevice) +{ + oc_endpoint_t ep1 = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer1 = addPeer(&ep1); + size_t deviceID1 = 1; + oc_sec_cred_t *role1 = oc_sec_roles_add(peer1, deviceID1); + ASSERT_NE(nullptr, role1); + oc_sec_cred_t *role2 = oc_sec_roles_add(peer1, deviceID1); + ASSERT_NE(nullptr, role2); + EXPECT_EQ(2, countRoles(peer1)); + + oc_endpoint_t ep2 = oc::endpoint::FromString("coaps://[::2]:42"); + const auto *peer2 = addPeer(&ep2); + ASSERT_NE(nullptr, peer2); + size_t deviceID2 = 2; + oc_sec_cred_t *role3 = oc_sec_roles_add(peer2, deviceID2); + ASSERT_NE(nullptr, role3); + EXPECT_EQ(1, countRoles(peer2)); + + size_t deviceID3 = 3; + EXPECT_EQ(0, oc_sec_free_roles_for_device(deviceID3)); + EXPECT_EQ(1, oc_sec_free_roles_for_device(deviceID2)); + EXPECT_EQ(2, oc_sec_free_roles_for_device(deviceID1)); +} + +TEST_F(TestRolesWithServer, FreeRoleByCredID) +{ + oc_endpoint_t ep1 = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer1 = addPeer(&ep1); + size_t deviceID1 = 1; + oc_sec_cred_t *role1 = oc_sec_roles_add(peer1, deviceID1); + ASSERT_NE(nullptr, role1); + role1->credid = 1; + oc_sec_cred_t *role2 = oc_sec_roles_add(peer1, deviceID1); + ASSERT_NE(nullptr, role2); + role2->credid = 2; + EXPECT_EQ(2, countRoles(peer1)); + + oc_endpoint_t ep2 = oc::endpoint::FromString("coaps://[::2]:42"); + const auto *peer2 = addPeer(&ep2); + ASSERT_NE(nullptr, peer2); + + EXPECT_FALSE(oc_sec_free_role_by_credid(1, peer2)); + EXPECT_FALSE(oc_sec_free_role_by_credid(3, peer1)); + EXPECT_TRUE(oc_sec_free_role_by_credid(1, peer1)); +} + +#else /* !OC_DYNAMIC_ALLOCATION */ + +TEST_F(TestRolesWithServer, AddRole_FailAllocation) +{ + oc_endpoint_t ep = oc::endpoint::FromString("coaps://[::1]:42"); + const auto *peer = addPeer(&ep); + ASSERT_NE(nullptr, peer); + + for (int i = 0; i < OC_MAX_NUM_DEVICES; ++i) { + oc_sec_cred_t *role = oc_sec_roles_add(peer, kDeviceID); + EXPECT_NE(nullptr, role); + } + + oc_sec_cred_t *role = oc_sec_roles_add(peer, kDeviceID); + EXPECT_EQ(nullptr, role); +} + +#endif /* OC_DYNAMIC_ALLOCATION */ + #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM -// TODO: add API to generate certificates -// TODO: GET /oic/sec/roles -> retrieve role certificates -// TODO: POST /oic/sec/roles -> add role certificate -// TODO: DELETE /oic/sec/roles -> remove role certificate +TEST_F(TestRolesWithServer, GetRequest) +{ + // get insecure connection to the testing device + const oc_endpoint_t *ep = + oc::TestDevice::GetEndpoint(/*device*/ 0, 0, SECURED); + ASSERT_NE(nullptr, ep); -#else /* !OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + auto get_handler = [](oc_client_response_t *data) { + EXPECT_EQ(OC_STATUS_OK, data->code); + oc::TestDevice::Terminate(); + OC_DBG("GET payload: %s", oc::RepPool::GetJson(data->payload).data()); + *static_cast(data->user_data) = true; + }; + + // TODO: need communication API to send add validlty role certificates and + // have a peer, connecting device to itself seems to break the TLS handshake + + bool invoked = false; + uint16_t timeout_s = 5; + EXPECT_TRUE(oc_do_get_with_timeout(OCF_SEC_ROLES_URI, ep, + "if=" OC_IF_BASELINE_STR, timeout_s, + get_handler, HIGH_QOS, &invoked)); + oc::TestDevice::PoolEvents(timeout_s); + + ASSERT_TRUE(invoked); +} + +TEST_F(TestRolesWithServer, PostRequest) +{ + // TODO: need communication API to send POST request, connecting device to + // itself seems to break the TLS handshake + + // roles_resource_post +} + +TEST_F(TestRolesWithServer, DeleteRequest) +{ + // get insecure connection to the testing device + const oc_endpoint_t *ep = + oc::TestDevice::GetEndpoint(/*device*/ 0, 0, SECURED); + ASSERT_NE(nullptr, ep); + + auto delete_handler = [](oc_client_response_t *data) { + EXPECT_EQ(OC_STATUS_DELETED, data->code); + oc::TestDevice::Terminate(); + *static_cast(data->user_data) = true; + }; + + bool invoked = false; + uint16_t timeout_s = 5; + EXPECT_TRUE(oc_do_delete_with_timeout(OCF_SEC_ROLES_URI, ep, nullptr, + timeout_s, delete_handler, HIGH_QOS, + &invoked)); + oc::TestDevice::PoolEvents(timeout_s); + ASSERT_TRUE(invoked); + + // TODO add roles and verify that they are all deleted +} + +TEST_F(TestRolesWithServer, DeleteRequest_FailInvalidCredid) +{ + const oc_endpoint_t *ep = + oc::TestDevice::GetEndpoint(/*device*/ 0, 0, SECURED); + ASSERT_NE(nullptr, ep); + + auto delete_handler = [](oc_client_response_t *data) { + EXPECT_EQ(OC_STATUS_NOT_FOUND, data->code); + oc::TestDevice::Terminate(); + *static_cast(data->user_data) = true; + }; + + // invalid format + std::string query = "credid=abc"; + bool invoked = false; + uint16_t timeout_s = 5; + EXPECT_TRUE(oc_do_delete_with_timeout(OCF_SEC_ROLES_URI, ep, query.c_str(), + timeout_s, delete_handler, HIGH_QOS, + &invoked)); + oc::TestDevice::PoolEvents(timeout_s); + ASSERT_TRUE(invoked); + + // negative + invoked = false; + query = "credid=-1"; + EXPECT_TRUE(oc_do_delete_with_timeout(OCF_SEC_ROLES_URI, ep, query.c_str(), + timeout_s, delete_handler, HIGH_QOS, + &invoked)); + oc::TestDevice::PoolEvents(timeout_s); + ASSERT_TRUE(invoked); + + // too big + invoked = false; + query = "credid=" + + std::to_string( + static_cast(std::numeric_limits::max()) + 1); + EXPECT_TRUE(oc_do_delete_with_timeout(OCF_SEC_ROLES_URI, ep, query.c_str(), + timeout_s, delete_handler, HIGH_QOS, + &invoked)); + oc::TestDevice::PoolEvents(timeout_s); + ASSERT_TRUE(invoked); + + // not found + invoked = false; + query = "credid=42"; + EXPECT_TRUE(oc_do_delete_with_timeout(OCF_SEC_ROLES_URI, ep, query.c_str(), + timeout_s, delete_handler, HIGH_QOS, + &invoked)); + oc::TestDevice::PoolEvents(timeout_s); + ASSERT_TRUE(invoked); +} #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ @@ -90,7 +578,7 @@ class TestRoleCreds : public testing::Test { { size_t count = 0; const oc_role_t *role = oc_sec_role_creds_get(); - while (role != NULL) { + while (role != nullptr) { ++count; role = role->next; }