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