diff --git a/.github/workflows/plgd-dps-tests.yml b/.github/workflows/plgd-dps-tests.yml index a2ad7fd65..06c8323f2 100644 --- a/.github/workflows/plgd-dps-tests.yml +++ b/.github/workflows/plgd-dps-tests.yml @@ -41,7 +41,7 @@ jobs: docker_file: docker/apps/Dockerfile.dps-cloud-server uses: ./.github/workflows/plgd-dps-test-with-cfg.yml with: - build_args: ${{ matrix.build_args }} + build_args: -DOC_DEBUG_ENABLED=ON -DPLGD_DPS_MAXIMUM_LOG_LEVEL=TRACE ${{ matrix.build_args }} build_type: ${{ (github.event_name == 'workflow_dispatch' && inputs.build_type) || 'Debug' }} docker_file: ${{ matrix.docker_file || 'docker/apps/Dockerfile.dps-cloud-server-debug' }} skip: ${{ matrix.skip || false }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e1c94f7a..bba6f2b86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,14 +236,13 @@ if(BUILD_MBEDTLS) endif() set(OC_LOG_MAXIMUM_LOG_LEVEL_INT) -oc_set_maximum_log_level(${OC_LOG_MAXIMUM_LOG_LEVEL} OC_LOG_MAXIMUM_LOG_LEVEL_INT) -set(OC_LOG_MAXIMUM_LEVEL ${OC_LOG_MAXIMUM_LOG_LEVEL_INT} CACHE INTERNAL "Maximum supported log level in compile time as integer.") +oc_set_maximum_log_level("${OC_LOG_MAXIMUM_LOG_LEVEL}" OC_LOG_MAXIMUM_LOG_LEVEL_INT) # clang-tidy triggers bugprone-macro-parentheses if the value is not in () -list(APPEND PRIVATE_COMPILE_DEFINITIONS "OC_LOG_MAXIMUM_LEVEL=(${OC_LOG_MAXIMUM_LEVEL})") -list(APPEND TEST_COMPILE_DEFINITIONS "OC_LOG_MAXIMUM_LEVEL=(${OC_LOG_MAXIMUM_LEVEL})") +list(APPEND PRIVATE_COMPILE_DEFINITIONS "OC_LOG_MAXIMUM_LEVEL=(${OC_LOG_MAXIMUM_LOG_LEVEL_INT})") +list(APPEND TEST_COMPILE_DEFINITIONS "OC_LOG_MAXIMUM_LEVEL=(${OC_LOG_MAXIMUM_LOG_LEVEL_INT})") if(BUILD_MBEDTLS) - list(APPEND MBEDTLS_COMPILE_DEFINITIONS "OC_LOG_MAXIMUM_LEVEL=(${OC_LOG_MAXIMUM_LEVEL})") + list(APPEND MBEDTLS_COMPILE_DEFINITIONS "OC_LOG_MAXIMUM_LEVEL=(${OC_LOG_MAXIMUM_LOG_LEVEL_INT})") endif() if(OC_PUSH_ENABLED) @@ -426,6 +425,12 @@ if(PLGD_DEV_DEVICE_PROVISIONING_ENABLED) message(FATAL_ERROR "Cannot set PLGD_DEV_DEVICE_PROVISIONING_ENABLED without PLGD_DEV_TIME_ENABLED") endif() list(APPEND PUBLIC_COMPILE_DEFINITIONS "PLGD_DEV_DEVICE_PROVISIONING") + + set(PLGD_DPS_MAXIMUM_LOG_LEVEL "DISABLED" CACHE STRING "Maximum supported Plgd DPS log level in compile time.") + set(PLGD_DPS_MAXIMUM_LOG_LEVEL_INT) + oc_set_maximum_log_level("${PLGD_DPS_MAXIMUM_LOG_LEVEL}" PLGD_DPS_MAXIMUM_LOG_LEVEL_INT) + list(APPEND PRIVATE_COMPILE_DEFINITIONS "PLGD_DPS_LOG_MAXIMUM_LEVEL=(${PLGD_DPS_MAXIMUM_LOG_LEVEL_INT})") + list(APPEND TEST_COMPILE_DEFINITIONS "PLGD_DPS_LOG_MAXIMUM_LEVEL=(${PLGD_DPS_MAXIMUM_LOG_LEVEL_INT})") endif() if(PLGD_DEV_DEVICE_PROVISIONING_TEST_PROPERTIES_ENABLED) diff --git a/api/plgd/device-provisioning-client/plgd_dps.c b/api/plgd/device-provisioning-client/plgd_dps.c index 3b08e7f74..6d2db25a2 100644 --- a/api/plgd/device-provisioning-client/plgd_dps.c +++ b/api/plgd/device-provisioning-client/plgd_dps.c @@ -18,13 +18,14 @@ #include "plgd_dps_apis_internal.h" #include "plgd_dps_endpoint_internal.h" +#include "plgd_dps_internal.h" #include "plgd_dps_log_internal.h" #include "plgd_dps_manager_internal.h" #include "plgd_dps_provision_internal.h" #include "plgd_dps_resource_internal.h" #include "plgd_dps_security_internal.h" #include "plgd_dps_store_internal.h" // dps_store_init -#include "plgd_dps_internal.h" +#include "plgd_dps_verify_certificate_internal.h" #include "api/oc_tcp_internal.h" #include "oc_certs.h" @@ -32,6 +33,7 @@ #include "oc_network_monitor.h" #include +#include static void dps_manager_status_cb(plgd_dps_context_t *ctx) diff --git a/api/plgd/device-provisioning-client/plgd_dps_endpoint_internal.h b/api/plgd/device-provisioning-client/plgd_dps_endpoint_internal.h index c7cf7012d..0996e15db 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_endpoint_internal.h +++ b/api/plgd/device-provisioning-client/plgd_dps_endpoint_internal.h @@ -24,6 +24,7 @@ extern "C" { #endif #include "plgd_dps_context_internal.h" +#include "plgd_dps_log_internal.h" #include "oc_config.h" #include "oc_endpoint.h" diff --git a/api/plgd/device-provisioning-client/plgd_dps_log_internal.h b/api/plgd/device-provisioning-client/plgd_dps_log_internal.h index ade478526..98511f93e 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_log_internal.h +++ b/api/plgd/device-provisioning-client/plgd_dps_log_internal.h @@ -49,12 +49,17 @@ extern "C" { #define PLGD_DPS_LOG_LEVEL_IS_ENABLED(level) \ ((level) <= (PLGD_DPS_LOG_MAXIMUM_LEVEL)) +#ifdef __cplusplus +#define DPS_LOG_CAST +#else +#define DPS_LOG_CAST (plgd_dps_log_component_t) +#endif + #define DPS_LOG(log_level, log_component, ...) \ do { \ if (plgd_dps_log_get_level() >= (log_level) && \ (plgd_dps_log_get_components() & log_component) != 0) { \ - plgd_dps_get_log_fn()((log_level), \ - (plgd_dps_log_component_t)(log_component), \ + plgd_dps_get_log_fn()((log_level), DPS_LOG_CAST(log_component), \ __FILENAME__, __LINE__, __func__, __VA_ARGS__); \ } \ } while (0) diff --git a/api/plgd/device-provisioning-client/plgd_dps_manager.c b/api/plgd/device-provisioning-client/plgd_dps_manager.c index 91789c748..06c9afebf 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_manager.c +++ b/api/plgd/device-provisioning-client/plgd_dps_manager.c @@ -31,6 +31,7 @@ #include "oc_cred.h" #include "oc_network_monitor.h" +#include "security/oc_cred_util_internal.h" #include "util/oc_list.h" #include @@ -53,20 +54,22 @@ dps_manager_start(plgd_dps_context_t *ctx) } static bool -dps_has_mfg_certificate(size_t device) +dps_mfg_certificate_iterate(const oc_sec_cred_t *cred, void *data) { - const oc_sec_cred_t *mfg_cred = NULL; - const oc_sec_creds_t *creds = oc_sec_get_creds(device); - const oc_sec_cred_t *cred = (oc_sec_cred_t *)oc_list_head(creds->creds); - while (cred != NULL) { - if (cred->credtype == OC_CREDTYPE_CERT && - cred->credusage == OC_CREDUSAGE_MFG_CERT && !dps_is_dps_cred(cred)) { - mfg_cred = cred; - break; - } - cred = cred->next; + if (cred->credtype == OC_CREDTYPE_CERT && + cred->credusage == OC_CREDUSAGE_MFG_CERT && !dps_is_dps_cred(cred)) { + (*(const oc_sec_cred_t **)data) = cred; + return false; } + return true; +} +static bool +dps_has_mfg_certificate(size_t device) +{ + const oc_sec_creds_t *creds = oc_sec_get_creds(device); + const oc_sec_cred_t *mfg_cred = NULL; + oc_cred_iterate(creds->creds, dps_mfg_certificate_iterate, &mfg_cred); if (mfg_cred != NULL) { DPS_DBG("Manufacturer certificate(%d) found", mfg_cred->credid); return true; @@ -74,20 +77,23 @@ dps_has_mfg_certificate(size_t device) return false; } +static bool +dps_mfg_trusted_root_ca_iterate(const oc_sec_cred_t *cred, void *data) +{ + if (cred->credtype == OC_CREDTYPE_CERT && + cred->credusage == OC_CREDUSAGE_MFG_TRUSTCA && !dps_is_dps_cred(cred)) { + (*(const oc_sec_cred_t **)data) = cred; + return false; + } + return true; +} + static bool dps_has_mfg_trusted_root_ca(size_t device) { const oc_sec_cred_t *trusted_ca = NULL; const oc_sec_creds_t *creds = oc_sec_get_creds(device); - const oc_sec_cred_t *cred = (oc_sec_cred_t *)oc_list_head(creds->creds); - while (cred != NULL) { - if (cred->credtype == OC_CREDTYPE_CERT && - cred->credusage == OC_CREDUSAGE_MFG_TRUSTCA && !dps_is_dps_cred(cred)) { - trusted_ca = cred; - break; - } - cred = cred->next; - } + oc_cred_iterate(creds->creds, dps_mfg_trusted_root_ca_iterate, &trusted_ca); if (trusted_ca != NULL) { DPS_DBG("manufacturer trusted root ca(%d) found", trusted_ca->credid); return true; @@ -95,10 +101,11 @@ dps_has_mfg_trusted_root_ca(size_t device) return false; } -static uint32_t -dps_try_reprovision(plgd_dps_context_t *ctx) +provision_and_cloud_observer_flags_t +dps_get_provision_and_cloud_observer_flags(plgd_dps_context_t *ctx) { uint32_t provisionFlags = 0; + uint8_t cloudObserverStatus = 0; if (dps_has_plgd_time()) { provisionFlags |= PLGD_DPS_HAS_TIME; } @@ -123,12 +130,15 @@ dps_try_reprovision(plgd_dps_context_t *ctx) if (dps_cloud_is_started(ctx->device)) { provisionFlags |= PLGD_DPS_CLOUD_STARTED; } - ctx->cloud_observer.last_status |= OC_CLOUD_REGISTERED; + cloudObserverStatus |= OC_CLOUD_REGISTERED; if (dps_cloud_is_logged_in(ctx->device)) { - ctx->cloud_observer.last_status |= OC_CLOUD_LOGGED_IN; + cloudObserverStatus |= OC_CLOUD_LOGGED_IN; } } - return provisionFlags; + return (provision_and_cloud_observer_flags_t){ + .provision_flags = provisionFlags, + .cloud_observer_status = cloudObserverStatus, + }; } int @@ -165,7 +175,10 @@ plgd_dps_manager_start(plgd_dps_context_t *ctx) ctx->status = 0; uint32_t new_status = PLGD_DPS_INITIALIZED; if (!ctx->force_reprovision) { - new_status |= dps_try_reprovision(ctx); + provision_and_cloud_observer_flags_t pacf = + dps_get_provision_and_cloud_observer_flags(ctx); + new_status |= pacf.provision_flags; + ctx->cloud_observer.last_status |= pacf.cloud_observer_status; } ctx->force_reprovision = false; dps_set_ps_and_last_error(ctx, new_status, 0, PLGD_DPS_OK); diff --git a/api/plgd/device-provisioning-client/plgd_dps_manager_internal.h b/api/plgd/device-provisioning-client/plgd_dps_manager_internal.h index 1db85bddc..4253c0419 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_manager_internal.h +++ b/api/plgd/device-provisioning-client/plgd_dps_manager_internal.h @@ -69,6 +69,16 @@ OC_NO_DISCARD_RETURN oc_event_callback_retval_t dps_manager_provision_retry_async(void *data) OC_NONNULL(); +typedef struct +{ + uint32_t provision_flags; + uint8_t cloud_observer_status; +} provision_and_cloud_observer_flags_t; + +/// @brief Get provision flags and cloud observer status based on current state +provision_and_cloud_observer_flags_t dps_get_provision_and_cloud_observer_flags( + plgd_dps_context_t *ctx) OC_NONNULL(); + #ifdef __cplusplus } #endif diff --git a/api/plgd/device-provisioning-client/plgd_dps_pki.c b/api/plgd/device-provisioning-client/plgd_dps_pki.c index f04b26fea..c06b3bd6c 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_pki.c +++ b/api/plgd/device-provisioning-client/plgd_dps_pki.c @@ -344,9 +344,7 @@ dps_pki_replace_credentials_handler(oc_client_response_t *data) dps_manager_reprovision_and_restart(ctx); } -/// @brief Try replacing current (expiring) DPS certificates with newer -/// certificates retrieved from the DPS service. -static bool +bool dps_pki_try_renew_certificates(plgd_dps_context_t *ctx) { assert(ctx != NULL); diff --git a/api/plgd/device-provisioning-client/plgd_dps_pki_internal.h b/api/plgd/device-provisioning-client/plgd_dps_pki_internal.h index e46c4ec24..37acd7e53 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_pki_internal.h +++ b/api/plgd/device-provisioning-client/plgd_dps_pki_internal.h @@ -139,6 +139,10 @@ OC_NO_DISCARD_RETURN bool dps_pki_replace_certificates(size_t device, const oc_rep_t *rep, const oc_endpoint_t *endpoint) OC_NONNULL(); +/// @brief Try replacing current (expiring) DPS certificates with newer +/// certificates retrieved from the DPS service. +bool dps_pki_try_renew_certificates(plgd_dps_context_t *ctx) OC_NONNULL(); + #ifdef __cplusplus } #endif diff --git a/api/plgd/device-provisioning-client/plgd_dps_provision_owner.c b/api/plgd/device-provisioning-client/plgd_dps_provision_owner.c index 2b07b01b2..176c73104 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_provision_owner.c +++ b/api/plgd/device-provisioning-client/plgd_dps_provision_owner.c @@ -39,11 +39,11 @@ #include -static int +int dps_handle_get_owner_response(oc_client_response_t *data) { const char *owner_str = NULL; - oc_rep_t *rep = data->payload; + const oc_rep_t *rep = data->payload; while (rep != NULL) { if (dps_is_property(rep, OC_REP_STRING, "devowneruuid", OC_CHAR_ARRAY_LEN("devowneruuid"))) { diff --git a/api/plgd/device-provisioning-client/plgd_dps_provision_owner_internal.h b/api/plgd/device-provisioning-client/plgd_dps_provision_owner_internal.h index 54e325bc5..afd58aba0 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_provision_owner_internal.h +++ b/api/plgd/device-provisioning-client/plgd_dps_provision_owner_internal.h @@ -44,6 +44,9 @@ extern "C" { */ bool dps_get_owner(plgd_dps_context_t *ctx) OC_NONNULL(); +/** Handler of get owner response */ +int dps_handle_get_owner_response(oc_client_response_t *data) OC_NONNULL(); + #if DPS_DBG_IS_ENABLED /// @brief Print owner of device, pstat and doxm resources, acls and diff --git a/api/plgd/device-provisioning-client/plgd_dps_retry.c b/api/plgd/device-provisioning-client/plgd_dps_retry.c index 5abb7450e..a016231a3 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_retry.c +++ b/api/plgd/device-provisioning-client/plgd_dps_retry.c @@ -17,6 +17,7 @@ ****************************************************************************/ #include "plgd_dps_context_internal.h" +#include "plgd_dps_internal.h" #include "plgd_dps_log_internal.h" #include "plgd_dps_retry_internal.h" diff --git a/api/plgd/device-provisioning-client/plgd_dps_security_internal.h b/api/plgd/device-provisioning-client/plgd_dps_security_internal.h index b384b7dfa..5f58abd4b 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_security_internal.h +++ b/api/plgd/device-provisioning-client/plgd_dps_security_internal.h @@ -28,7 +28,7 @@ #include "security/oc_tls_internal.h" #if DPS_DBG_IS_ENABLED -#include "mbedlts/build_info.h" +#include "mbedtls/build_info.h" #include "mbedtls/md.h" #endif /* DPS_DBG_IS_ENABLED */ diff --git a/api/plgd/device-provisioning-client/plgd_dps_verify_certificate.c b/api/plgd/device-provisioning-client/plgd_dps_verify_certificate.c index b2e7682bf..a98cc1911 100644 --- a/api/plgd/device-provisioning-client/plgd_dps_verify_certificate.c +++ b/api/plgd/device-provisioning-client/plgd_dps_verify_certificate.c @@ -146,6 +146,13 @@ dps_verify_certificate(oc_tls_peer_t *peer, const mbedtls_x509_crt *crt, return -1; } + dps_verify_certificate_data_t *cb_data = + (dps_verify_certificate_data_t *)peer->user_data.data; + if (cb_data == NULL) { + DPS_ERR("verifying certificate - cb_data is NULL"); + return -1; + } + unsigned char fingerprint[MBEDTLS_MD_MAX_SIZE] = { 0 }; /* buffer is max length of returned hash, which is 64 in case we use sha-512 */ @@ -153,12 +160,6 @@ dps_verify_certificate(oc_tls_peer_t *peer, const mbedtls_x509_crt *crt, if (!calculate_fingerprint(ctx, crt, fingerprint, &fingerprint_size)) { return -1; } - dps_verify_certificate_data_t *cb_data = - (dps_verify_certificate_data_t *)peer->user_data.data; - if (cb_data == NULL) { - DPS_ERR("verifying certificate - cb_data is NULL"); - return -1; - } // check fingerprint every time if (ctx->certificate_fingerprint.md_type != MBEDTLS_MD_NONE && diff --git a/api/plgd/unittest/plgd_dps_apis.cpp b/api/plgd/unittest/plgd_dps_apis.cpp index 6090f0bef..6c2ab72b0 100644 --- a/api/plgd/unittest/plgd_dps_apis.cpp +++ b/api/plgd/unittest/plgd_dps_apis.cpp @@ -35,11 +35,7 @@ static constexpr size_t kDeviceID = 0; class DPSApisTest : public testing::Test { public: - static void SetUpTestCase() - { - oc_runtime_init(); - plgd_dps_log_set_level(OC_LOG_LEVEL_TRACE); - } + static void SetUpTestCase() { oc_runtime_init(); } static void TearDownTestCase() { oc_runtime_shutdown(); } }; diff --git a/api/plgd/unittest/plgd_dps_endpoint.cpp b/api/plgd/unittest/plgd_dps_endpoint.cpp index 2108bef4e..df5684733 100644 --- a/api/plgd/unittest/plgd_dps_endpoint.cpp +++ b/api/plgd/unittest/plgd_dps_endpoint.cpp @@ -25,14 +25,9 @@ #include "oc_core_res.h" #include "oc_helpers.h" #include "oc_uuid.h" -#include "security/oc_pstat_internal.h" -#include "security/oc_tls_internal.h" -#include "tests/gtest/Device.h" #include "gtest/gtest.h" -#include "mbedtls/x509_crt.h" -#include #include #include #include @@ -80,127 +75,4 @@ TEST(DPSApiTest, EndpointToString) EXPECT_STREQ(exp_str.c_str(), out.data()); } -class DPSTLSPeerTest : public testing::Test { -protected: - void SetUp() override - { - EXPECT_TRUE(oc::TestDevice::StartServer()); - size_t deviceCount = oc::TestDevice::CountDevices(); - ASSERT_LT(0, deviceCount); - deviceId_ = deviceCount - 1; - - oc_sec_pstat_t *pstat = oc_sec_get_pstat(deviceId_); - pstat->s = OC_DOS_RFNOP; - plgd_dps_init(); - } - - void TearDown() override - { - plgd_dps_shutdown(); - oc::TestDevice::StopServer(); - } - - size_t GetDeviceId() const { return deviceId_; } - -private: - size_t deviceId_{ static_cast(-1) }; - -public: - static oc_endpoint_t getEndpoint(const std::string &ep) - { - oc_string_t ep_str; - oc_new_string(&ep_str, ep.c_str(), ep.length()); - oc_endpoint_t endpoint; - EXPECT_EQ(0, oc_string_to_endpoint(&ep_str, &endpoint, nullptr)); - oc_free_string(&ep_str); - return endpoint; - } -}; - -TEST_F(DPSTLSPeerTest, VerifyCertificate) -{ - oc_endpoint_t ep = getEndpoint("coaps://[ff02::43]:1338"); - - std::string data = "c8:21:63:6f:61:70:73:2b:74:63:70:3a:2f:2f:6d:6f:63:6b:2e:" - "70:6c:67:64:2e:63:6c:6f:75:64:3a:32:36:" - "36:38:34:c9:20:a1:e1:c3:4c:3e:3:17:8d:e4:77:79:f9:92:28:" - "7d:fe:b4:b7:70:2f:80:ee:d9:15:dd:ec:d6:" - "54:e4:c6:4f:e2:ca:6:53:48:41:32:35:36"; - ssize_t ret = - plgd_dps_hex_string_to_bytes(data.c_str(), data.length(), nullptr, 0); - ASSERT_EQ(77, ret); - std::array buf; - ret = plgd_dps_hex_string_to_bytes(data.c_str(), data.length(), &buf[0], ret); - ASSERT_EQ(77, ret); - EXPECT_EQ(PLGD_DPS_DHCP_SET_VALUES_NEED_REPROVISION, - plgd_dps_dhcp_set_values_from_vendor_encapsulated_options( - plgd_dps_get_context(GetDeviceId()), &buf[0], ret)); - - /* - * Convert a certificate from PEM to hex for embedding into C-code - * $ openssl x509 -outform der -in certificate.pem -out certificate.der - * $ xxd -i certificate.der - */ - std::array certificate = { - 0x30, 0x82, 0x01, 0x8d, 0x30, 0x82, 0x01, 0x32, 0xa0, 0x03, 0x02, 0x01, - 0x02, 0x02, 0x11, 0x00, 0xa7, 0x5b, 0x88, 0x93, 0x82, 0x6a, 0xba, 0xf8, - 0x60, 0xfd, 0xf6, 0x38, 0x8a, 0xca, 0xc9, 0xa1, 0x30, 0x0a, 0x06, 0x08, - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x12, 0x31, 0x10, - 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x07, 0x70, 0x6c, 0x67, - 0x64, 0x2d, 0x63, 0x61, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x31, 0x32, - 0x30, 0x31, 0x31, 0x31, 0x33, 0x38, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x32, - 0x33, 0x31, 0x32, 0x30, 0x31, 0x31, 0x31, 0x33, 0x38, 0x33, 0x37, 0x5a, - 0x30, 0x1a, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, - 0x0f, 0x6d, 0x6f, 0x63, 0x6b, 0x2e, 0x70, 0x6c, 0x67, 0x64, 0x2e, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, - 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, - 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x31, 0xe7, 0xc9, 0x43, 0xbf, - 0xd7, 0xfb, 0x88, 0x91, 0x78, 0xad, 0xcc, 0x0b, 0xd1, 0x54, 0xe4, 0x50, - 0xac, 0xb2, 0xbb, 0x6b, 0xa0, 0xec, 0x56, 0x6e, 0x96, 0x6d, 0x34, 0x6b, - 0xde, 0x03, 0x2f, 0x6a, 0x9c, 0x8e, 0x15, 0x2c, 0x1b, 0x37, 0x8e, 0x78, - 0x30, 0xe8, 0x7d, 0xba, 0xbe, 0x43, 0x33, 0x87, 0xab, 0x5e, 0x33, 0xe9, - 0x87, 0xe3, 0x32, 0x4a, 0xa5, 0x7e, 0xe5, 0x8e, 0xd6, 0x47, 0x78, 0xa3, - 0x61, 0x30, 0x5f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, - 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, - 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0c, - 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, - 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, - 0x14, 0xb3, 0x75, 0xa7, 0xac, 0x25, 0x64, 0x51, 0x5d, 0xe6, 0x15, 0x5d, - 0x15, 0x16, 0xe2, 0xe8, 0x5f, 0xff, 0xc9, 0x3d, 0x91, 0x30, 0x0f, 0x06, - 0x03, 0x55, 0x1d, 0x11, 0x04, 0x08, 0x30, 0x06, 0x87, 0x04, 0x7f, 0x00, - 0x00, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, - 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xc0, 0x7d, - 0xe9, 0x7b, 0x47, 0x40, 0x8f, 0x0e, 0xfe, 0x86, 0x17, 0xf8, 0xbd, 0xde, - 0x4a, 0x60, 0x34, 0x9b, 0xca, 0x97, 0xcc, 0x8e, 0xed, 0x55, 0xd3, 0xbe, - 0xcb, 0xdc, 0x37, 0xa5, 0x48, 0x75, 0x02, 0x21, 0x00, 0x93, 0x88, 0x3e, - 0x53, 0x7c, 0xa6, 0x1e, 0xc9, 0x04, 0x9a, 0x6d, 0xf8, 0x4f, 0x72, 0xd3, - 0x7a, 0x84, 0x11, 0xad, 0xef, 0x4e, 0x76, 0xb5, 0x87, 0xe1, 0x1e, 0x97, - 0x0e, 0x21, 0xfb, 0x94, 0xd0 - }; - - oc_tls_peer_t *peer = dps_endpoint_add_peer(&ep); - ASSERT_NE(nullptr, peer); - ASSERT_NE(nullptr, peer->verify_certificate); - - mbedtls_x509_crt crt; - memset(&crt, 0, sizeof(mbedtls_x509_crt)); - - // validate intermediate certificate - uint32_t flags = MBEDTLS_X509_BADCERT_NOT_TRUSTED; - ASSERT_EQ(0, peer->verify_certificate(peer, &crt, 1, &flags)); - ASSERT_EQ(0, flags); - - // validate leaf certificate - flags = MBEDTLS_X509_BADCERT_NOT_TRUSTED; - ASSERT_EQ(-1, peer->verify_certificate(peer, &crt, 0, &flags)); - ASSERT_EQ(MBEDTLS_X509_BADCERT_NOT_TRUSTED, flags); - - // validate leaf with good certificate - crt.raw.p = &certificate[0]; - crt.raw.len = certificate.size(); - flags = MBEDTLS_X509_BADCERT_NOT_TRUSTED; - ASSERT_EQ(0, peer->verify_certificate(peer, &crt, 0, &flags)); - ASSERT_EQ(0, flags); -} - #endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/api/plgd/unittest/plgd_dps_log.cpp b/api/plgd/unittest/plgd_dps_log.cpp index 4f5b4d858..b3e6cafa6 100644 --- a/api/plgd/unittest/plgd_dps_log.cpp +++ b/api/plgd/unittest/plgd_dps_log.cpp @@ -137,8 +137,10 @@ TEST_F(TestDPSLog, LogByComponent) plgd_dps_set_log_fn(expectComponents); DPS_ERR_COMPONENT(PLGD_DPS_LOG_COMPONENT_DEFAULT, "default error"); - DPS_ERR_COMPONENT(kLogComponent_1, "component_1 error"); - DPS_ERR_COMPONENT(kLogComponent_2, "component_2 error"); + DPS_ERR_COMPONENT(static_cast(kLogComponent_1), + "component_1 error"); + DPS_ERR_COMPONENT(static_cast(kLogComponent_2), + "component_2 error"); } static void diff --git a/api/plgd/unittest/plgd_dps_manager.cpp b/api/plgd/unittest/plgd_dps_manager.cpp index a7e34365c..117c9fab2 100644 --- a/api/plgd/unittest/plgd_dps_manager.cpp +++ b/api/plgd/unittest/plgd_dps_manager.cpp @@ -20,8 +20,16 @@ #ifdef OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING +#include "api/cloud/oc_cloud_context_internal.h" +#include "api/oc_helpers_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_manager_internal.h" #include "api/plgd/device-provisioning-client/plgd_dps_provision_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_security_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_tag_internal.h" +#include "oc_acl.h" +#include "oc_cred.h" #include "plgd_dps_test.h" +#include "plgd/plgd_time.h" #include "tests/gtest/Device.h" #include "gtest/gtest.h" @@ -32,16 +40,14 @@ static constexpr size_t kDeviceID = 0; using namespace std::chrono_literals; -class DPSManagerTest : public testing::Test { +class TestDPSManager : public testing::Test { public: static void SetUpTestCase() { ASSERT_TRUE(oc::TestDevice::StartServer()); } static void TearDownTestCase() { oc::TestDevice::StopServer(); } - - void SetUp() override { plgd_dps_log_set_level(OC_LOG_LEVEL_TRACE); } }; -TEST_F(DPSManagerTest, ChangeEndpointOnRetry) +TEST_F(TestDPSManager, ChangeEndpointOnRetry) { auto ctx = dps::make_unique_context(kDeviceID); @@ -78,4 +84,105 @@ TEST_F(DPSManagerTest, ChangeEndpointOnRetry) EXPECT_TRUE(selected); } +TEST_F(TestDPSManager, StartAlreadyStarted) +{ + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int mfg_credid = + dps::addIdentityCertificate(kDeviceID, identKey, rootKey, true); + ASSERT_LT(0, mfg_credid); + + plgd_dps_context_t ctx{}; + dps_context_init(&ctx, kDeviceID); + ctx.skip_verify = true; + dps_context_list_add(&ctx); + + std::string ep_uri = "coap://127.0.0.1:12345"; + ASSERT_NE(nullptr, plgd_dps_add_endpoint_address( + &ctx, ep_uri.c_str(), ep_uri.length(), nullptr, 0)); + + EXPECT_EQ(0, plgd_dps_manager_start(&ctx)); + EXPECT_EQ(0, plgd_dps_manager_start(&ctx)); + + plgd_dps_manager_stop(&ctx); + dps_context_list_remove(&ctx); + ASSERT_TRUE(oc_sec_remove_cred_by_credid(mfg_credid, kDeviceID)); +} + +TEST_F(TestDPSManager, GetProvisionAndCloudObserverFlags) +{ + plgd_time_set_time(oc_clock_time()); + + plgd_dps_context_t ctx{}; + auto pof = dps_get_provision_and_cloud_observer_flags(&ctx); + uint32_t provision_flags = PLGD_DPS_HAS_TIME; + uint8_t cloud_observer_status = 0; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + oc_uuid_t owner; + oc_gen_uuid(&owner); + ASSERT_TRUE(dps_set_owner(&ctx, &owner)); + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + provision_flags |= PLGD_DPS_HAS_OWNER; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + auto *cloud_ctx = oc_cloud_get_context(kDeviceID); + ASSERT_NE(nullptr, cloud_ctx); + std::string at{ "access_token" }; + oc_new_string(&cloud_ctx->store.access_token, at.c_str(), at.length()); + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + provision_flags |= PLGD_DPS_HAS_CLOUD; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + +#ifdef OC_DYNAMIC_ALLOCATION + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int root_credid = dps::addRootCertificate(kDeviceID, rootKey, false, true); + ASSERT_LT(0, root_credid); + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int credid = + dps::addIdentityCertificate(kDeviceID, identKey, rootKey, false, true); + ASSERT_LT(0, credid); + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + provision_flags |= PLGD_DPS_HAS_CREDENTIALS; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + ASSERT_TRUE(oc_sec_acl_add_bootstrap_acl(kDeviceID)); + auto *ace = (oc_sec_ace_t *)oc_list_head(oc_sec_get_acl(0)->subjects); + EXPECT_NE(nullptr, ace); + oc_set_string(&ace->tag, DPS_TAG, DPS_TAG_LEN); + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + provision_flags |= PLGD_DPS_HAS_ACLS; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + cloud_ctx->store.status = OC_CLOUD_REGISTERED; + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + cloud_observer_status |= OC_CLOUD_REGISTERED; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + cloud_ctx->cloud_manager = true; + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + provision_flags |= PLGD_DPS_CLOUD_STARTED; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + cloud_ctx->store.status |= OC_CLOUD_LOGGED_IN; + pof = dps_get_provision_and_cloud_observer_flags(&ctx); + cloud_observer_status |= OC_CLOUD_LOGGED_IN; + EXPECT_EQ(provision_flags, pof.provision_flags); + EXPECT_EQ(cloud_observer_status, pof.cloud_observer_status); + + ASSERT_TRUE(oc_sec_remove_cred_by_credid(credid, kDeviceID)); + ASSERT_TRUE(oc_sec_remove_cred_by_credid(root_credid, kDeviceID)); +#endif /* OC_DYNAMIC_ALLOCATION */ + + plgd_time_set_time(0); + plgd_time_set_status(PLGD_TIME_STATUS_IN_SYNC); +} + #endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/api/plgd/unittest/plgd_dps_pki.cpp b/api/plgd/unittest/plgd_dps_pki.cpp index f24c31dd5..10ff9754a 100644 --- a/api/plgd/unittest/plgd_dps_pki.cpp +++ b/api/plgd/unittest/plgd_dps_pki.cpp @@ -21,7 +21,10 @@ #ifdef OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING #include "api/oc_runtime_internal.h" +#include "api/oc_server_api_internal.h" +#include "api/cloud/oc_cloud_context_internal.h" #include "api/plgd/device-provisioning-client/plgd_dps_context_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_internal.h" #include "api/plgd/device-provisioning-client/plgd_dps_pki_internal.h" #include "oc_rep.h" #include "tests/gtest/Device.h" @@ -37,11 +40,9 @@ using namespace std::chrono_literals; static constexpr size_t kDeviceID = 0; class TestPKI : public testing::Test { -protected: +public: void SetUp() override { oc_runtime_init(); } void TearDown() override { oc_runtime_shutdown(); } - -public: }; TEST_F(TestPKI, SendCSR_FailInvalidDeviceID) @@ -123,4 +124,44 @@ TEST_F(TestPKIWithDevice, ReplaceCertificates_FailInvalidRep) EXPECT_FALSE(dps_pki_replace_certificates(kDeviceID, rep.get(), &emptyEp)); } +TEST_F(TestPKIWithDevice, TryRenewCertificates) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.device = kDeviceID; + EXPECT_TRUE(dps_pki_try_renew_certificates(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + plgd_dps_manager_stop(&ctx); +} + +TEST_F(TestPKIWithDevice, RenewCertificates) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.device = kDeviceID; + ctx.status = PLGD_DPS_PROVISIONED_MASK | PLGD_DPS_CLOUD_STARTED; + + oc_cloud_context_t *cloud_ctx = oc_cloud_get_context(kDeviceID); + ASSERT_NE(nullptr, cloud_ctx); + cloud_ctx->cloud_manager = true; + + oc_reset_delayed_callback(&ctx, dps_pki_renew_certificates_async, 0); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + plgd_dps_manager_stop(&ctx); +} + #endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/api/plgd/unittest/plgd_dps_provision_owner.cpp b/api/plgd/unittest/plgd_dps_provision_owner.cpp new file mode 100644 index 000000000..6a5ca7ad0 --- /dev/null +++ b/api/plgd/unittest/plgd_dps_provision_owner.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** + * + * Copyright (c) 2022-2024 plgd.dev, s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING + +#include "api/plgd/device-provisioning-client/plgd_dps_context_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_provision_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_provision_owner_internal.h" +#include "oc_api.h" +#include "oc_rep.h" +#include "security/oc_pstat_internal.h" +#include "tests/gtest/Device.h" +#include "tests/gtest/RepPool.h" + +#include "gtest/gtest.h" + +using namespace std::chrono_literals; + +static constexpr size_t kDeviceID = 0; + +class TestProvisionOwnerWithDevice : public testing::Test { +public: + static void SetUpTestCase() { ASSERT_TRUE(oc::TestDevice::StartServer()); } + + static void TearDownTestCase() { oc::TestDevice::StopServer(); } + + void SetUp() override + { + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFNOP; + } + + void TearDown() override + { + oc::TestDevice::Reset(); + oc::TestDevice::CloseSessions(kDeviceID); + // wait for asynchronous closing of sessions to finish + oc::TestDevice::PoolEventsMsV1(10ms); + oc::TestDevice::ClearSystemTime(); + } +}; + +TEST_F(TestProvisionOwnerWithDevice, GetOwner_FailInvalidDOSState) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + plgd_dps_context_t ctx{}; + ctx.device = 42; + EXPECT_FALSE(dps_get_owner(&ctx)); + + pstat->s = OC_DOS_RFNOP; +} + +TEST_F(TestProvisionOwnerWithDevice, GetOwner_OwnerAlreadySet) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.status = PLGD_DPS_HAS_OWNER; + EXPECT_TRUE(dps_get_owner(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + oc_has_delayed_callback(nullptr, dps_provision_next_step_async, true); +} + +TEST_F(TestProvisionOwnerWithDevice, GetOwner_InvalidStatus) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.status = PLGD_DPS_INITIALIZED; + EXPECT_TRUE(dps_get_owner(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + EXPECT_EQ(PLGD_DPS_INITIALIZED | PLGD_DPS_GET_OWNER | PLGD_DPS_FAILURE, + ctx.status); + EXPECT_EQ(PLGD_DPS_ERROR_GET_OWNER, ctx.last_error); +} + +TEST_F(TestProvisionOwnerWithDevice, GetOwner_InvalidResponse) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.status = PLGD_DPS_INITIALIZED | PLGD_DPS_HAS_TIME; + EXPECT_TRUE(dps_get_owner(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + EXPECT_EQ(PLGD_DPS_INITIALIZED | PLGD_DPS_HAS_TIME | PLGD_DPS_GET_OWNER | + PLGD_DPS_FAILURE, + ctx.status); + EXPECT_EQ(PLGD_DPS_ERROR_RESPONSE, ctx.last_error); +} + +TEST_F(TestProvisionOwnerWithDevice, HandleGetOwnerResponse_Fail) +{ + oc_client_response_t response{}; + // owner property not set + EXPECT_EQ(-1, dps_handle_get_owner_response(&response)); + + // unexpected property + std::string uuid = "00000000-0000-0000-0000-000000000001"; + + oc::RepPool pool{}; + oc_rep_start_root_object(); + oc_rep_set_text_string_v1(root, rowneruuid, uuid.c_str(), uuid.length()); + oc_rep_end_root_object(); + ASSERT_EQ(CborNoError, oc_rep_get_cbor_errno()); + + auto rep = pool.ParsePayload(); + response.payload = rep.get(); + EXPECT_EQ(-1, dps_handle_get_owner_response(&response)); +} + +#endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/api/plgd/unittest/plgd_dps_tag.cpp b/api/plgd/unittest/plgd_dps_tag.cpp index eba5a77b1..d0157c46f 100644 --- a/api/plgd/unittest/plgd_dps_tag.cpp +++ b/api/plgd/unittest/plgd_dps_tag.cpp @@ -25,6 +25,7 @@ #include "oc_api.h" #include "oc_cred.h" #include "oc_pki.h" +#include "plgd_dps_test.h" #include "gtest/gtest.h" @@ -64,89 +65,12 @@ class TestDPSTag : public testing::Test { #ifdef OC_DYNAMIC_ALLOCATION -static bool -is_directory(const std::string &path) -{ - struct stat statbuf; - if (stat(path.c_str(), &statbuf) != 0) { - return false; - } - return S_ISDIR(statbuf.st_mode) != 0; -} - -static int -read_pem(const std::string &file_path, std::vector &buffer) -{ - FILE *file = fopen(file_path.c_str(), "r"); - if (file == nullptr) { - return -1; - } - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return -1; - } - long pem_len = ftell(file); - if (pem_len < 0) { - fclose(file); - return -1; - } - if (fseek(file, 0, SEEK_SET) != 0) { - fclose(file); - return -1; - } - buffer.resize(pem_len); - if (fread(&buffer[0], 1, pem_len, file) < (size_t)pem_len) { - fclose(file); - return -1; - } - fclose(file); - buffer.push_back('\0'); - return 0; -} - -static int -add_mfg_certificate(size_t device, const std::string &cert_dir) -{ - int mfg_credid = -1; - std::string path = cert_dir + "/ee.pem"; - std::vector mfg_crt = {}; - std::vector mfg_key = {}; - oc_sec_cred_t *cred; - if (read_pem(path, mfg_crt) < 0) { - goto error; - } - path = cert_dir + "/key.pem"; - if (read_pem(path, mfg_key) < 0) { - goto error; - } - mfg_credid = oc_pki_add_mfg_cert(device, mfg_crt.data(), mfg_crt.size(), - mfg_key.data(), mfg_key.size()); - if (mfg_credid < 0) { - goto error; - } - - cred = oc_sec_get_cred_by_credid(mfg_credid, device); - if (cred == nullptr) { - goto error; - } - - oc_set_string(&cred->tag, DPS_TAG, DPS_TAG_LEN); - return mfg_credid; -error: - if (mfg_credid != -1) { - oc_sec_remove_cred_by_credid(mfg_credid, device); - } - return -1; -} - TEST_F(TestDPSTag, TagCredentials) { - std::string cert_dir = "pki_certs"; - if (!is_directory(cert_dir)) { - return; - } - - int mfg_credid = add_mfg_certificate(0, cert_dir); + oc::keypair_t rootKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + oc::keypair_t identKey{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; + int mfg_credid = + dps::addIdentityCertificate(0, identKey, rootKey, true, true); ASSERT_LT(0, mfg_credid); dps_credentials_set_stale_tag(0); @@ -159,7 +83,7 @@ TEST_F(TestDPSTag, TagCredentials) ASSERT_NE(nullptr, cred); EXPECT_STREQ(DPS_TAG, oc_string(cred->tag)); - oc_sec_remove_cred_by_credid(mfg_credid, 0); + ASSERT_TRUE(oc_sec_remove_cred_by_credid(mfg_credid, 0)); } #endif /* OC_DYNAMIC_ALLOCATION */ diff --git a/api/plgd/unittest/plgd_dps_test.cpp b/api/plgd/unittest/plgd_dps_test.cpp new file mode 100644 index 000000000..2ff3fbc40 --- /dev/null +++ b/api/plgd/unittest/plgd_dps_test.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** + * + * Copyright (c) 2022-2024 plgd.dev, s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING + +#include "api/plgd/device-provisioning-client/plgd_dps_tag_internal.h" +#include "oc_cred.h" +#include "oc_pki.h" +#include "plgd_dps_test.h" +#include "tests/gtest/PKI.h" + +#include + +namespace dps { + +context_unique_ptr +make_unique_context(size_t device) +{ + auto ctxUPtr = + context_unique_ptr{ new plgd_dps_context_t, [](plgd_dps_context_t *ctx) { + dps_context_deinit(ctx); + delete ctx; + } }; + memset(ctxUPtr.get(), 0, sizeof(plgd_dps_context_t)); + dps_context_init(ctxUPtr.get(), device); + return ctxUPtr; +} + +#ifdef OC_DYNAMIC_ALLOCATION + +static bool +addTag(size_t device, int credid) +{ + oc_sec_cred_t *cred = oc_sec_get_cred_by_credid(credid, device); + if (cred == nullptr) { + return false; + } + oc_set_string(&cred->tag, DPS_TAG, DPS_TAG_LEN); + return true; +} + +int +addRootCertificate(size_t device, const oc::keypair_t &kp, bool is_mfg, + bool add_tag) +{ + auto pem = oc::pki::GenerateRootCertificate(kp); + int credid = is_mfg + ? oc_pki_add_mfg_trust_anchor(device, pem.data(), pem.size()) + : oc_pki_add_trust_anchor(device, pem.data(), pem.size()); + if ((credid < 0) || (add_tag && !addTag(device, credid))) { + goto error; + } + return credid; + +error: + if (credid != -1) { + oc_sec_remove_cred_by_credid(credid, device); + } + return -1; +} + +int +addIdentityCertificate(size_t device, const oc::keypair_t &kp, + const oc::keypair_t &issuer_kp, bool is_mfg, + bool add_tag) +{ + auto pem = oc::pki::GeneratIdentityCertificate(kp, issuer_kp); + if (pem.empty()) { + return -1; + } + oc::pki::KeyParser parser{}; + auto keyPem = + parser.GetPrivateKey(kp.private_key.data(), kp.private_key_size); + if (keyPem.empty()) { + return -1; + } + + int credid = is_mfg ? oc_pki_add_mfg_cert(device, pem.data(), pem.size(), + keyPem.data(), keyPem.size()) + : oc_pki_add_identity_cert(device, pem.data(), pem.size(), + keyPem.data(), keyPem.size()); + if ((credid < 0) || (add_tag && !addTag(device, credid))) { + goto error; + } + return credid; +error: + if (credid != -1) { + oc_sec_remove_cred_by_credid(credid, device); + } + return -1; +} + +#endif /* OC_DYNAMIC_ALLOCATION */ + +} // namespace dps + +#endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/api/plgd/unittest/plgd_dps_test.h b/api/plgd/unittest/plgd_dps_test.h index 29fe085b4..b6f65cee0 100644 --- a/api/plgd/unittest/plgd_dps_test.h +++ b/api/plgd/unittest/plgd_dps_test.h @@ -19,26 +19,30 @@ #pragma once #include "api/plgd/device-provisioning-client/plgd_dps_context_internal.h" +#include "tests/gtest/KeyPair.h" +#include "util/oc_features.h" -#include #include +#include +#include +#include namespace dps { using context_unique_ptr = std::unique_ptr; -static context_unique_ptr -make_unique_context(size_t device) -{ - auto ctxUPtr = - context_unique_ptr{ new plgd_dps_context_t, [](plgd_dps_context_t *ctx) { - dps_context_deinit(ctx); - delete ctx; - } }; - memset(ctxUPtr.get(), 0, sizeof(plgd_dps_context_t)); - dps_context_init(ctxUPtr.get(), device); - return ctxUPtr; -} +context_unique_ptr make_unique_context(size_t device); + +#ifdef OC_DYNAMIC_ALLOCATION + +int addRootCertificate(size_t device, const oc::keypair_t &kp, + bool is_mfg = false, bool add_tag = false); + +int addIdentityCertificate(size_t device, const oc::keypair_t &kp, + const oc::keypair_t &issuer_kp, bool is_mfg = false, + bool add_tag = false); + +#endif /* OC_DYNAMIC_ALLOCATION */ } // namespace dps diff --git a/api/plgd/unittest/plgd_dps_time.cpp b/api/plgd/unittest/plgd_dps_time.cpp new file mode 100644 index 000000000..2113b6fd1 --- /dev/null +++ b/api/plgd/unittest/plgd_dps_time.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** + * + * Copyright (c) 2022-2024 plgd.dev, s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING + +#include "api/plgd/device-provisioning-client/plgd_dps_context_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_provision_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_time_internal.h" +#include "security/oc_pstat_internal.h" +#include "tests/gtest/Device.h" + +#include "gtest/gtest.h" + +using namespace std::chrono_literals; + +static constexpr size_t kDeviceID = 0; + +class TestDPSTimeWithDevice : public testing::Test { +public: + static void SetUpTestCase() { ASSERT_TRUE(oc::TestDevice::StartServer()); } + + static void TearDownTestCase() { oc::TestDevice::StopServer(); } + + void SetUp() override + { + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFNOP; + } + + void TearDown() override + { + oc::TestDevice::Reset(); + oc::TestDevice::CloseSessions(kDeviceID); + // wait for asynchronous closing of sessions to finish + oc::TestDevice::PoolEventsMsV1(10ms); + oc::TestDevice::ClearSystemTime(); + } +}; + +TEST_F(TestDPSTimeWithDevice, GetTime_FailInvalidDOSState) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + plgd_dps_context_t ctx{}; + ctx.device = 42; + EXPECT_FALSE(dps_get_plgd_time(&ctx)); + + pstat->s = OC_DOS_RFNOP; +} + +TEST_F(TestDPSTimeWithDevice, GetTime_OwnerAlreadySet) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.status = PLGD_DPS_HAS_TIME; + EXPECT_TRUE(dps_get_plgd_time(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + oc_has_delayed_callback(nullptr, dps_provision_next_step_async, true); +} + +TEST_F(TestDPSTimeWithDevice, GetTime_InvalidStatus) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.status = PLGD_DPS_INITIALIZED | PLGD_DPS_GET_OWNER; + EXPECT_TRUE(dps_get_plgd_time(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + EXPECT_EQ(PLGD_DPS_INITIALIZED | PLGD_DPS_GET_TIME | PLGD_DPS_GET_OWNER | + PLGD_DPS_FAILURE, + ctx.status); + EXPECT_EQ(PLGD_DPS_ERROR_GET_TIME, ctx.last_error); +} + +TEST_F(TestDPSTimeWithDevice, GetTime_InvalidResponse) +{ + auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); + ASSERT_TRUE(epOpt.has_value()); + auto ep = std::move(*epOpt); + + plgd_dps_context_t ctx{}; + ctx.endpoint = &ep; + ctx.status = PLGD_DPS_INITIALIZED; + EXPECT_TRUE(dps_get_plgd_time(&ctx)); + + auto timeout = 10ms; + oc::TestDevice::PoolEventsMsV1(timeout, true); + + EXPECT_EQ(PLGD_DPS_INITIALIZED | PLGD_DPS_GET_TIME | PLGD_DPS_FAILURE, + ctx.status); + EXPECT_EQ(PLGD_DPS_ERROR_RESPONSE, ctx.last_error); +} + +#endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/api/plgd/unittest/plgd_dps_verify_certificate.cpp b/api/plgd/unittest/plgd_dps_verify_certificate.cpp new file mode 100644 index 000000000..65cfcd0c0 --- /dev/null +++ b/api/plgd/unittest/plgd_dps_verify_certificate.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** + * + * Copyright (c) 2022-2024 plgd.dev, s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "util/oc_features.h" + +#ifdef OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING + +#include "api/plgd/device-provisioning-client/plgd_dps_endpoint_internal.h" +#include "api/plgd/device-provisioning-client/plgd_dps_verify_certificate_internal.h" +#include "plgd_dps_test.h" +#include "security/oc_pstat_internal.h" +#include "security/oc_tls_internal.h" +#include "tests/gtest/Device.h" + +#include "gtest/gtest.h" +#include "mbedtls/x509_crt.h" + +#include +#include + +static constexpr size_t kDeviceID = 0; + +class TestVerifyCertificate : public testing::Test {}; + +TEST_F(TestVerifyCertificate, AllocateVerifyCertificateData) +{ + dps_verify_certificate_data_free(nullptr); + + oc_pki_user_data_t pud{}; + pud.data = malloc(42); + pud.free = free; + + dps_verify_certificate_data_t *dvcd = + dps_verify_certificate_data_new({ pud, nullptr }); + + dps_verify_certificate_data_free(dvcd); +} + +TEST_F(TestVerifyCertificate, VerifyCertificate_FailInvalidDevice) +{ + oc_tls_peer_t peer{}; + peer.endpoint.device = 42; + mbedtls_x509_crt crt{}; + uint32_t flags{}; + EXPECT_EQ(-1, dps_verify_certificate(&peer, &crt, 0, &flags)); +} + +TEST_F(TestVerifyCertificate, VerifyCertificate_FailMissingPeerContext) +{ + auto ctx = dps::make_unique_context(kDeviceID); + dps_context_list_add(ctx.get()); + + oc_tls_peer_t peer{}; + peer.endpoint.device = kDeviceID; + mbedtls_x509_crt crt{}; + uint32_t flags{}; + EXPECT_EQ(-1, dps_verify_certificate(&peer, &crt, 0, &flags)); + + dps_context_list_remove(ctx.get()); +} + +TEST_F(TestVerifyCertificate, VerifyCertificate_FailInvalidFingerprint) +{ + auto ctx = dps::make_unique_context(kDeviceID); + dps_context_list_add(ctx.get()); + + oc_tls_peer_t peer{}; + peer.endpoint.device = kDeviceID; + dps_verify_certificate_data_t vcd{}; + peer.user_data.data = &vcd; + mbedtls_x509_crt crt{}; + uint32_t flags{}; + + // unsupported algorithm + ctx->certificate_fingerprint.md_type = MBEDTLS_MD_MD5; + EXPECT_EQ(-1, dps_verify_certificate(&peer, &crt, 0, &flags)); + dps_context_list_remove(ctx.get()); +} + +class TestVerifyCertificateWithDevice : public testing::Test { +protected: + void SetUp() override + { + EXPECT_TRUE(oc::TestDevice::StartServer()); + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + pstat->s = OC_DOS_RFNOP; + plgd_dps_init(); + } + + void TearDown() override + { + plgd_dps_shutdown(); + oc::TestDevice::StopServer(); + } + +public: + static oc_endpoint_t getEndpoint(const std::string &ep) + { + oc_string_t ep_str; + oc_new_string(&ep_str, ep.c_str(), ep.length()); + oc_endpoint_t endpoint; + EXPECT_EQ(0, oc_string_to_endpoint(&ep_str, &endpoint, nullptr)); + oc_free_string(&ep_str); + return endpoint; + } +}; + +TEST_F(TestVerifyCertificateWithDevice, VerifyCertificate) +{ + oc_endpoint_t ep = getEndpoint("coaps://[ff02::43]:1338"); + + std::string data = "c8:21:63:6f:61:70:73:2b:74:63:70:3a:2f:2f:6d:6f:63:6b:2e:" + "70:6c:67:64:2e:63:6c:6f:75:64:3a:32:36:" + "36:38:34:c9:20:a1:e1:c3:4c:3e:3:17:8d:e4:77:79:f9:92:28:" + "7d:fe:b4:b7:70:2f:80:ee:d9:15:dd:ec:d6:" + "54:e4:c6:4f:e2:ca:6:53:48:41:32:35:36"; + ssize_t ret = + plgd_dps_hex_string_to_bytes(data.c_str(), data.length(), nullptr, 0); + ASSERT_EQ(77, ret); + std::array buf; + ret = plgd_dps_hex_string_to_bytes(data.c_str(), data.length(), &buf[0], ret); + ASSERT_EQ(77, ret); + EXPECT_EQ(PLGD_DPS_DHCP_SET_VALUES_NEED_REPROVISION, + plgd_dps_dhcp_set_values_from_vendor_encapsulated_options( + plgd_dps_get_context(kDeviceID), &buf[0], ret)); + + /* + * Convert a certificate from PEM to hex for embedding into C-code + * $ openssl x509 -outform der -in certificate.pem -out certificate.der + * $ xxd -i certificate.der + */ + std::array certificate = { + 0x30, 0x82, 0x01, 0x8d, 0x30, 0x82, 0x01, 0x32, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x11, 0x00, 0xa7, 0x5b, 0x88, 0x93, 0x82, 0x6a, 0xba, 0xf8, + 0x60, 0xfd, 0xf6, 0x38, 0x8a, 0xca, 0xc9, 0xa1, 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x12, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x07, 0x70, 0x6c, 0x67, + 0x64, 0x2d, 0x63, 0x61, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x31, 0x32, + 0x30, 0x31, 0x31, 0x31, 0x33, 0x38, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x32, + 0x33, 0x31, 0x32, 0x30, 0x31, 0x31, 0x31, 0x33, 0x38, 0x33, 0x37, 0x5a, + 0x30, 0x1a, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0f, 0x6d, 0x6f, 0x63, 0x6b, 0x2e, 0x70, 0x6c, 0x67, 0x64, 0x2e, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x31, 0xe7, 0xc9, 0x43, 0xbf, + 0xd7, 0xfb, 0x88, 0x91, 0x78, 0xad, 0xcc, 0x0b, 0xd1, 0x54, 0xe4, 0x50, + 0xac, 0xb2, 0xbb, 0x6b, 0xa0, 0xec, 0x56, 0x6e, 0x96, 0x6d, 0x34, 0x6b, + 0xde, 0x03, 0x2f, 0x6a, 0x9c, 0x8e, 0x15, 0x2c, 0x1b, 0x37, 0x8e, 0x78, + 0x30, 0xe8, 0x7d, 0xba, 0xbe, 0x43, 0x33, 0x87, 0xab, 0x5e, 0x33, 0xe9, + 0x87, 0xe3, 0x32, 0x4a, 0xa5, 0x7e, 0xe5, 0x8e, 0xd6, 0x47, 0x78, 0xa3, + 0x61, 0x30, 0x5f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0c, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xb3, 0x75, 0xa7, 0xac, 0x25, 0x64, 0x51, 0x5d, 0xe6, 0x15, 0x5d, + 0x15, 0x16, 0xe2, 0xe8, 0x5f, 0xff, 0xc9, 0x3d, 0x91, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x1d, 0x11, 0x04, 0x08, 0x30, 0x06, 0x87, 0x04, 0x7f, 0x00, + 0x00, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, + 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xc0, 0x7d, + 0xe9, 0x7b, 0x47, 0x40, 0x8f, 0x0e, 0xfe, 0x86, 0x17, 0xf8, 0xbd, 0xde, + 0x4a, 0x60, 0x34, 0x9b, 0xca, 0x97, 0xcc, 0x8e, 0xed, 0x55, 0xd3, 0xbe, + 0xcb, 0xdc, 0x37, 0xa5, 0x48, 0x75, 0x02, 0x21, 0x00, 0x93, 0x88, 0x3e, + 0x53, 0x7c, 0xa6, 0x1e, 0xc9, 0x04, 0x9a, 0x6d, 0xf8, 0x4f, 0x72, 0xd3, + 0x7a, 0x84, 0x11, 0xad, 0xef, 0x4e, 0x76, 0xb5, 0x87, 0xe1, 0x1e, 0x97, + 0x0e, 0x21, 0xfb, 0x94, 0xd0 + }; + + oc_tls_peer_t *peer = dps_endpoint_add_peer(&ep); + ASSERT_NE(nullptr, peer); + ASSERT_NE(nullptr, peer->verify_certificate); + + mbedtls_x509_crt crt; + memset(&crt, 0, sizeof(mbedtls_x509_crt)); + + // validate intermediate certificate + uint32_t flags = MBEDTLS_X509_BADCERT_NOT_TRUSTED; + ASSERT_EQ(0, peer->verify_certificate(peer, &crt, 1, &flags)); + ASSERT_EQ(0, flags); + + // validate leaf certificate + flags = MBEDTLS_X509_BADCERT_NOT_TRUSTED; + ASSERT_EQ(-1, peer->verify_certificate(peer, &crt, 0, &flags)); + ASSERT_EQ(MBEDTLS_X509_BADCERT_NOT_TRUSTED, flags); + + // validate leaf with good certificate + crt.raw.p = &certificate[0]; + crt.raw.len = certificate.size(); + flags = MBEDTLS_X509_BADCERT_NOT_TRUSTED; + ASSERT_EQ(0, peer->verify_certificate(peer, &crt, 0, &flags)); + ASSERT_EQ(0, flags); +} + +#endif /* OC_HAS_FEATURE_PLGD_DEVICE_PROVISIONING */ diff --git a/tools/utils.cmake b/tools/utils.cmake index 230bf606b..cc502c889 100644 --- a/tools/utils.cmake +++ b/tools/utils.cmake @@ -48,19 +48,19 @@ function(oc_add_compile_options) endfunction() function(oc_set_maximum_log_level level outlevel) - if(${level} STREQUAL "DISABLED") + if(("${level}" STREQUAL "DISABLED") OR ("${level}" STREQUAL "")) set(level_int -1) - elseif(${level} STREQUAL "ERROR") + elseif("${level}" STREQUAL "ERROR") set(level_int 3) - elseif(${level} STREQUAL "WARNING") + elseif("${level}" STREQUAL "WARNING") set(level_int 4) - elseif(${level} STREQUAL "NOTICE") + elseif("${level}" STREQUAL "NOTICE") set(level_int 5) - elseif(${level} STREQUAL "INFO") + elseif("${level}" STREQUAL "INFO") set(level_int 6) - elseif(${level} STREQUAL "DEBUG") + elseif("${level}" STREQUAL "DEBUG") set(level_int 7) - elseif(${level} STREQUAL "TRACE") + elseif("${level}" STREQUAL "TRACE") set(level_int 8) else() message(FATAL_ERROR "Invalid log level string: ${level}")