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/api/oc_base64.c b/api/oc_base64.c index 7ad865346e..3e6a0822bb 100644 --- a/api/oc_base64.c +++ b/api/oc_base64.c @@ -128,7 +128,8 @@ oc_base64_decode(uint8_t *str, size_t len) /* The Base64 input string is decoded in-place. */ size_t i = 0; int j = 0; - unsigned char val_c = 0, val_s = 0; + unsigned char val_c = 0; + unsigned char val_s = 0; /* Process every input character */ for (i = 0; i < len; i++) { diff --git a/api/oc_client_api.c b/api/oc_client_api.c index 5e31d1a867..eb7cdc5916 100644 --- a/api/oc_client_api.c +++ b/api/oc_client_api.c @@ -167,7 +167,7 @@ prepare_coap_request(oc_client_cb_t *cb) if (cb->method == OC_PUT || cb->method == OC_POST) { g_request_buffer = oc_blockwise_alloc_request_buffer( oc_string(cb->uri) + 1, oc_string_len(cb->uri) - 1, &cb->endpoint, - cb->method, OC_BLOCKWISE_CLIENT, OC_MIN_APP_DATA_SIZE); + cb->method, OC_BLOCKWISE_CLIENT, (uint32_t)OC_MIN_APP_DATA_SIZE); if (!g_request_buffer) { OC_ERR("g_request_buffer is NULL"); return false; diff --git a/api/oc_client_role.c b/api/oc_client_role.c index 89bfb8003e..e6cc10d618 100644 --- a/api/oc_client_role.c +++ b/api/oc_client_role.c @@ -27,7 +27,7 @@ oc_role_t * oc_get_all_roles(void) { - return oc_sec_get_role_creds(); + return oc_sec_role_creds_get(); } static void @@ -74,7 +74,7 @@ oc_assert_role(const char *role, const char *authority, return false; } oc_tls_select_cert_ciphersuite(); - if (!oc_init_post("/oic/sec/roles", endpoint, NULL, handler, HIGH_QOS, + if (!oc_init_post(OCF_SEC_ROLES_URI, endpoint, NULL, handler, HIGH_QOS, user_data)) { OC_ERR("cannot init POST"); } @@ -94,7 +94,7 @@ void oc_assert_all_roles(const oc_endpoint_t *endpoint, oc_response_handler_t handler, void *user_data) { - oc_tls_peer_t *peer = oc_tls_get_peer(endpoint); + const oc_tls_peer_t *peer = oc_tls_get_peer(endpoint); if (oc_tls_uses_psk_cred(peer)) { return; } @@ -103,7 +103,7 @@ oc_assert_all_roles(const oc_endpoint_t *endpoint, if (roles == NULL) { return; } - if (!oc_init_post("/oic/sec/roles", endpoint, NULL, handler, HIGH_QOS, + if (!oc_init_post(OCF_SEC_ROLES_URI, endpoint, NULL, handler, HIGH_QOS, user_data)) { OC_ERR("cannot init POST"); } diff --git a/api/oc_collection.c b/api/oc_collection.c index f4d10f46ce..72e43e0cf6 100644 --- a/api/oc_collection.c +++ b/api/oc_collection.c @@ -832,7 +832,8 @@ oc_handle_collection_batch_request(oc_method_t method, oc_request_t *request, assert(request != NULL); int ecode = oc_status_code(OC_STATUS_OK); int pcode = oc_status_code(OC_STATUS_BAD_REQUEST); - CborEncoder encoder, prev_link; + CborEncoder encoder; + CborEncoder prev_link; oc_request_t rest_request = { 0 }; oc_response_t response = { 0 }; oc_response_buffer_t response_buffer; diff --git a/api/oc_core_res.c b/api/oc_core_res.c index 9da38e1712..bf7d88d962 100644 --- a/api/oc_core_res.c +++ b/api/oc_core_res.c @@ -16,10 +16,11 @@ * ****************************************************************************/ -#include "oc_core_res.h" #include "messaging/coap/oc_coap.h" #include "oc_api.h" +#include "oc_core_res.h" #include "oc_core_res_internal.h" +#include "oc_csr.h" #include "oc_discovery.h" #include "oc_endpoint.h" #include "oc_introspection_internal.h" @@ -28,6 +29,7 @@ #include "oc_ri_internal.h" #include "oc_main.h" #include "oc_server_api_internal.h" +#include "oc_swupdate_internal.h" #include "port/oc_assert.h" #include "util/oc_atomic.h" #include "util/oc_compiler.h" @@ -45,6 +47,7 @@ #ifdef OC_SECURITY #include "security/oc_doxm_internal.h" #include "security/oc_pstat.h" +#include "security/oc_roles_internal.h" #include "security/oc_sdi_internal.h" #include "security/oc_sp_internal.h" #include "security/oc_tls_internal.h" @@ -205,8 +208,9 @@ oc_core_device_handler(oc_request_t *request, oc_interface_mask_t iface_mask, size_t device = request->resource->device; oc_rep_start_root_object(); - char di[OC_UUID_LEN], piid[OC_UUID_LEN]; + char di[OC_UUID_LEN]; oc_uuid_to_str(&g_oc_device_info[device].di, di, OC_UUID_LEN); + char piid[OC_UUID_LEN]; if (request->origin && request->origin->version != OIC_VER_1_1_0) { oc_uuid_to_str(&g_oc_device_info[device].piid, piid, OC_UUID_LEN); } @@ -873,12 +877,12 @@ oc_core_get_resource_type_by_uri(const char *uri) return OCF_SEC_SP; } #ifdef OC_PKI - if (core_is_resource_uri(uri, uri_len, "/oic/sec/csr", - OC_CHAR_ARRAY_LEN("/oic/sec/csr"))) { + if (core_is_resource_uri(uri, uri_len, OCF_SEC_CSR_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_CSR_URI))) { return OCF_SEC_CSR; } - if (core_is_resource_uri(uri, uri_len, "/oic/sec/roles", - OC_CHAR_ARRAY_LEN("/oic/sec/roles"))) { + if (core_is_resource_uri(uri, uri_len, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { return OCF_SEC_ROLES; } #endif /* OC_PKI */ @@ -888,8 +892,8 @@ oc_core_get_resource_type_by_uri(const char *uri) } #endif /* OC_SECURITY */ #ifdef OC_SOFTWARE_UPDATE - if (core_is_resource_uri(uri, uri_len, "/oc/swu", - OC_CHAR_ARRAY_LEN("/oc/swu"))) { + if (core_is_resource_uri(uri, uri_len, OCF_SW_UPDATE_URI, + OC_CHAR_ARRAY_LEN(OCF_SW_UPDATE_URI))) { return OCF_SW_UPDATE; } #endif /* OC_SOFTWARE_UPDATE */ diff --git a/api/oc_discovery.c b/api/oc_discovery.c index 75a3bd2062..394aab18bd 100644 --- a/api/oc_discovery.c +++ b/api/oc_discovery.c @@ -1052,12 +1052,12 @@ oc_discovery_process_payload(const uint8_t *payload, size_t len, OC_MEMB_LOCAL(rep_objects, oc_rep_t, OC_MAX_NUM_REP_OBJECTS); oc_rep_set_pool(&rep_objects); - oc_rep_t *links = 0, *rep, *p; + oc_rep_t *p = NULL; int s = oc_parse_rep(payload, len, &p); if (s != 0) { OC_WRN("error parsing discovery response"); } - links = rep = p; + oc_rep_t *rep = p; /* While the oic.wk.res schema over the baseline interface provides for an * array of objects, only one object is present and used in practice. * @@ -1070,6 +1070,7 @@ oc_discovery_process_payload(const uint8_t *payload, size_t len, rep = rep->value.object; } + oc_rep_t *links = p; while (rep != NULL) { switch (rep->type) { /* Ignore other oic.wk.res properties over here as they're known diff --git a/api/oc_main.c b/api/oc_main.c index ba845b435e..d8b1a2f1d4 100644 --- a/api/oc_main.c +++ b/api/oc_main.c @@ -48,6 +48,7 @@ #include "security/oc_tls_internal.h" #ifdef OC_PKI #include "security/oc_keypair_internal.h" +#include "security/oc_roles_internal.h" #endif /* OC_PKI */ #include "security/oc_sdi_internal.h" #endif /* OC_SECURITY */ @@ -445,6 +446,9 @@ oc_main_shutdown(void) oc_sec_svr_free(); #ifdef OC_PKI +#ifdef OC_CLIENT + oc_sec_role_creds_free(); +#endif /* OC_CLIENT */ oc_sec_ecdsa_free_keypairs(); #endif /* OC_PKI */ #endif /* OC_SECURITY */ diff --git a/api/oc_resource_factory.c b/api/oc_resource_factory.c index 8f8695fb07..8f19a33e1e 100644 --- a/api/oc_resource_factory.c +++ b/api/oc_resource_factory.c @@ -145,7 +145,7 @@ get_collection_instance_uri(oc_collection_t *collection, char *uri, if (index == 0) { return false; } - unsigned len = oc_string_len(collection->res.uri) + 1; + size_t len = oc_string_len(collection->res.uri) + 1; if (len > uri_size) { // uri too long for output buffer return false; @@ -157,14 +157,14 @@ get_collection_instance_uri(oc_collection_t *collection, char *uri, uri[oc_string_len(collection->res.uri)] = '/'; int written = snprintf(NULL, 0, "%d", index); - if ((written <= 0) || (len + (unsigned)written + 1 > uri_size)) { + if ((written <= 0) || (len + (size_t)written + 1 > uri_size)) { // cannot fit the index converted to string into uri return false; } written = snprintf(uri + len, uri_size - len, "%d", index); // check for truncation by snprintf - return (written > 0) && ((unsigned)written <= uri_size - len); + return (written > 0) && ((size_t)written <= uri_size - len); } oc_rt_created_t * diff --git a/api/oc_ri.c b/api/oc_ri.c index 983541410a..b37f248212 100644 --- a/api/oc_ri.c +++ b/api/oc_ri.c @@ -848,9 +848,9 @@ oc_ri_audit_log(oc_method_t method, const oc_resource_t *resource, snprintf(aux[idx++], LINE_WIDTH, "device is in %s", state_str_val[state]); snprintf(aux[idx++], LINE_WIDTH, "No roles asserted"); #ifdef OC_PKI - if (peer) { + if (peer != NULL) { size_t pos = 0; - for (oc_sec_cred_t *rc = oc_sec_get_roles(peer); rc && pos < LINE_WIDTH; + for (oc_sec_cred_t *rc = oc_sec_roles_get(peer); rc && pos < LINE_WIDTH; rc = rc->next) { pos += snprintf(aux[idx - 1] + pos, LINE_WIDTH - pos - 1, "%s ", oc_string(rc->role.role)); @@ -1194,7 +1194,7 @@ ri_invoke_coap_entity_set_response(coap_packet_t *response, if (response_buffer->code == oc_status_code(OC_STATUS_REQUEST_ENTITY_TOO_LARGE)) { - coap_set_header_size1(response, OC_BLOCK_SIZE); + coap_set_header_size1(response, (uint32_t)OC_BLOCK_SIZE); } /* response_buffer->code at this point contains a valid CoAP status @@ -1393,7 +1393,7 @@ oc_ri_invoke_coap_entity_handler(const coap_packet_t *request, OC_DBG("creating new block-wise response state"); *ctx.response_state = oc_blockwise_alloc_response_buffer( uri_path, uri_path_len, endpoint, method, OC_BLOCKWISE_SERVER, - OC_MIN_APP_DATA_SIZE); + (uint32_t)OC_MIN_APP_DATA_SIZE); if (*ctx.response_state == NULL) { OC_ERR("failure to alloc response state"); bad_request = true; diff --git a/api/oc_server_api.c b/api/oc_server_api.c index 63f4c5fe22..2aa4c4b168 100644 --- a/api/oc_server_api.c +++ b/api/oc_server_api.c @@ -844,7 +844,8 @@ oc_notify_observers_delayed_ticks(oc_resource_t *resource, return; } oc_remove_delayed_callback(resource, &oc_observe_notification_delayed); - oc_set_delayed_callback(resource, &oc_observe_notification_delayed, ticks); + oc_ri_add_timed_event_callback_ticks(resource, + &oc_observe_notification_delayed, ticks); } void diff --git a/api/oc_uuid.c b/api/oc_uuid.c index 8d5948cb4e..28743d1170 100644 --- a/api/oc_uuid.c +++ b/api/oc_uuid.c @@ -82,7 +82,7 @@ oc_str_to_uuid(const char *str, oc_uuid_t *uuid) uuid->id[j++] = c; c = 0; } else { - c = c << 4; + c = (uint8_t)(c << 4); } k++; } diff --git a/api/unittest/reptest.cpp b/api/unittest/reptest.cpp index 6cfed01ffe..f6c6aacfe6 100644 --- a/api/unittest/reptest.cpp +++ b/api/unittest/reptest.cpp @@ -235,9 +235,9 @@ TEST_F(TestRepWithPool, OCRepSetGetInt) EXPECT_FALSE(oc_rep_get_int(rep.get(), "zero", nullptr)); EXPECT_FALSE(oc_rep_get_int(rep.get(), "not_a_key", &zero_out)); - CheckJson(rep.get(), - "{\"ultimate_answer\":10000000000,\"negative\":-1024,\"zero\":0}", - false); + std::string json = + R"({"ultimate_answer":10000000000,"negative":-1024,"zero":0})"; + CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"ultimate_answer\" : 10000000000,\n" " \"negative\" : -1024,\n" @@ -301,7 +301,7 @@ TEST_F(TestRepWithPool, OCRepSetGetUint) EXPECT_FALSE(oc_rep_get_int(rep.get(), "not_a_key", &zero_out)); std::string json = - "{\"ultimate_answer\":42,\"larger_than_int\":3000000000,\"zero\":0}"; + R"({"ultimate_answer":42,"larger_than_int":3000000000,"zero":0})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"ultimate_answer\" : 42,\n" @@ -341,7 +341,7 @@ TEST_F(TestRepWithPool, OCRepSetGetBool) EXPECT_FALSE(oc_rep_get_bool(rep.get(), "true_flag", nullptr)); EXPECT_FALSE(oc_rep_get_bool(rep.get(), "not_a_key", &true_flag_out)); - std::string json = "{\"true_flag\":true,\"false_flag\":false}"; + std::string json = R"({"true_flag":true,"false_flag":false})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"true_flag\" : true,\n" @@ -407,9 +407,8 @@ TEST_F(TestRepWithPool, OCRepSetGetTextString) EXPECT_FALSE( oc_rep_get_string(rep.get(), "not_a_key", &hal9000_out, &str_len)); - std::string json = "{\"empty\":\"\"," - "\"hal9000\":\"Dave\"," - "\"ru_character_set\":\"Привет, мир\"}"; + std::string json = + R"({"empty":"","hal9000":"Dave","ru_character_set":"Привет, мир"})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"empty\" : \"\",\n" @@ -468,8 +467,8 @@ TEST_F(TestRepWithPool, OCRepSetGetByteString) EXPECT_FALSE(oc_rep_get_byte_string(rep.get(), "not_a_key", &test_byte_string_out, &str_len)); - std::string json = "{\"empty_byte_string\":\"\"," - "\"test_byte_string\":\"AQIDBAIA\"}"; + std::string json = + R"({"empty_byte_string":"","test_byte_string":"AQIDBAIA"})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"empty_byte_string\" : \"\",\n" @@ -484,7 +483,7 @@ TEST_F(TestRepWithPool, OCRepSetGetByteString) // Decoding of byte string is an all or nothing action. Since there // is not enough room in the too_small output buffer nothing is placed in the // buffer and remaining space is left empty. - std::string too_small_json = "{\"empty_byte_string\":\"\","; + std::string too_small_json = R"({"empty_byte_string":"",)"; EXPECT_STREQ(too_small_json.c_str(), too_small.data()); } @@ -932,7 +931,7 @@ TEST_F(TestRepWithPool, OCRepSetGetObject) EXPECT_EQ(5, c_out_size); EXPECT_STREQ("three", c_out); - std::string json = "{\"my_object\":{\"a\":1,\"b\":false,\"c\":\"three\"}}"; + std::string json = R"({"my_object":{"a":1,"b":false,"c":"three"}})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"my_object\" : {\n" @@ -1120,9 +1119,9 @@ TEST_F(TestRepWithPool, OCRepSetGetObjectArray) EXPECT_STREQ("AI computer", job_out); std::string json = - "{\"space_2001\":[{\"name\":\"Dave Bowman\"," - "\"job\":\"astronaut\"},{\"name\":\"Frank Poole\",\"job\":\"astronaut\"}" - ",{\"name\":\"Hal 9000\",\"job\":\"AI computer\"}]}"; + R"({"space_2001":[{"name":"Dave Bowman","job":"astronaut"},)" + R"({"name":"Frank Poole","job":"astronaut"},)" + R"({"name":"Hal 9000","job":"AI computer"}]})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"space_2001\" : [\n" @@ -1209,8 +1208,8 @@ TEST_F(TestRepWithPool, OCRepAddGetByteStringArray) 0); std::string json = - "{\"barray\":[\"AQECAwQFBg==\"," - "\"AQECAwUIEyE0VYk=\",\"QkJCQkJCQkJCQkJCQkJCQkJCQkI=\",\"AAD/AAA=\"]}"; + R"({"barray":["AQECAwQFBg==",)" + R"("AQECAwUIEyE0VYk=","QkJCQkJCQkJCQkJCQkJCQkJCQkI=","AAD/AAA="]})"; CheckJson(rep.get(), json, false); std::string pretty_json = "{\n" " \"barray\" : [\n" @@ -1314,11 +1313,11 @@ TEST_F(TestRepWithPool, OCRepSetGetStringArray) EXPECT_STREQ(STR3.c_str(), oc_string_array_get_item(quotes_out, 3)); // clang-format off - std::string json = "{\"quotes\":" - "[\"" + STR0 + "\"," - "\"" + STR1 + "\"," - "\"" + STR2 + "\"," - "\"" + STR3 + "\"]}"; + std::string json = R"({"quotes":)" + R"([")" + STR0 + R"(",)" + R"(")" + STR1 + R"(",)" + R"(")" + STR2 + R"(",)" + R"(")" + STR3 + R"("]})"; // clang-format on CheckJson(rep.get(), json, false); // clang-format off @@ -1501,8 +1500,8 @@ TEST_F(TestRepWithPool, OCRepRootArrayObject) EXPECT_TRUE(oc_rep_get_int(rep_out, "count", &count_out)); EXPECT_EQ(100, count_out); - std::string json = "[{\"href\":\"/light/1\",\"rep\":{\"state\":true}}," - "{\"href\":\"/count/1\",\"rep\":{\"count\":100}}]"; + std::string json = R"([{"href":"/light/1","rep":{"state":true}},)" + R"({"href":"/count/1","rep":{"count":100}}])"; CheckJson(rep.get(), json, false); std::string pretty_json = "[\n" " {\n" diff --git a/api/unittest/resourcetest.cpp b/api/unittest/resourcetest.cpp index 00a4cc1ffb..91cce45074 100644 --- a/api/unittest/resourcetest.cpp +++ b/api/unittest/resourcetest.cpp @@ -40,7 +40,6 @@ class TestResourceWithDevice : public testing::Test { #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM oc_resource_t *con = oc_core_get_resource_by_index(OCF_CON, /*device*/ 0); ASSERT_NE(nullptr, con); - // oc_resource_make_public(con); oc_resource_set_access_in_RFOTM(con, true, OC_PERM_RETRIEVE); #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ } @@ -135,7 +134,7 @@ TEST_F(TestResourceWithDevice, BaselineInterfaceProperties) auto get_handler = [](oc_client_response_t *data) { ASSERT_EQ(OC_STATUS_OK, data->code); oc::TestDevice::Terminate(); - bool *invoked = static_cast(data->user_data); + auto *invoked = static_cast(data->user_data); *invoked = true; oc_rep_t *rep = data->payload; diff --git a/include/oc_helpers.h b/include/oc_helpers.h index 0e0328992f..a172348edc 100644 --- a/include/oc_helpers.h +++ b/include/oc_helpers.h @@ -177,9 +177,9 @@ void oc_concat_strings(oc_string_t *concat, const char *str1, const char *str2); #define oc_double_array(ocdoublearray) (oc_cast(ocdoublearray, double)) #ifdef OC_DYNAMIC_ALLOCATION -#define STRING_ARRAY_ITEM_MAX_LEN 128 +#define STRING_ARRAY_ITEM_MAX_LEN (128) #else /* OC_DYNAMIC_ALLOCATION */ -#define STRING_ARRAY_ITEM_MAX_LEN 32 +#define STRING_ARRAY_ITEM_MAX_LEN (32) #endif /* !OC_DYNAMIC_ALLOCATION */ bool _oc_copy_string_to_array(oc_string_array_t *ocstringarray, diff --git a/messaging/coap/coap.c b/messaging/coap/coap.c index d5483c5b15..2ad21b2e23 100644 --- a/messaging/coap/coap.c +++ b/messaging/coap/coap.c @@ -195,7 +195,8 @@ coap_set_option_header(unsigned int delta, size_t length, uint8_t *buffer) size_t written = 0; if (buffer) { - buffer[0] = coap_option_nibble(delta) << 4 | coap_option_nibble(length); + buffer[0] = + (uint8_t)(coap_option_nibble(delta) << 4 | coap_option_nibble(length)); } if (delta > 268) { @@ -1187,7 +1188,8 @@ coap_tcp_parse_message_length(const uint8_t *data, size_t *message_length, *message_length = tcp_len; } else { uint8_t i = 1; - *num_extended_length_bytes = 1 << (tcp_len - COAP_TCP_EXTENDED_LENGTH_1); + *num_extended_length_bytes = + (uint8_t)(1 << (tcp_len - COAP_TCP_EXTENDED_LENGTH_1)); for (i = 1; i <= *num_extended_length_bytes; i++) { *message_length |= ((uint32_t)(0x000000FF & data[i]) << (8 * (*num_extended_length_bytes - i))); @@ -1412,7 +1414,7 @@ coap_send_message(oc_message_t *message) message->endpoint.version == OCF_VER_1_0_0) { tcp_csm_state_t state = oc_tcp_get_csm_state(&message->endpoint); if (state == CSM_NONE) { - coap_send_csm_message(&message->endpoint, OC_PDU_SIZE, 0); + coap_send_csm_message(&message->endpoint, (uint32_t)OC_PDU_SIZE, 0); } } #endif /* OC_TCP */ diff --git a/messaging/coap/engine.c b/messaging/coap/engine.c index c8bb0029e4..e5e3efa605 100644 --- a/messaging/coap/engine.c +++ b/messaging/coap/engine.c @@ -196,11 +196,12 @@ coap_receive(oc_message_t *msg) bool block2 = false; #ifdef OC_BLOCK_WISE - oc_blockwise_state_t *request_buffer = NULL, *response_buffer = NULL; + oc_blockwise_state_t *request_buffer = NULL; + oc_blockwise_state_t *response_buffer = NULL; #endif /* OC_BLOCK_WISE */ #ifdef OC_CLIENT - oc_client_cb_t *client_cb = 0; + oc_client_cb_t *client_cb = NULL; #endif /* OC_CLIENT */ coap_status_t status; @@ -339,10 +340,10 @@ coap_receive(oc_message_t *msg) OC_WRN("cannot process new request during closing TLS sessions"); goto init_reset_message; } - uint32_t buffer_size = OC_MAX_APP_DATA_SIZE; + uint32_t buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; if (coap_get_header_size1(message, &buffer_size) && buffer_size == 0) { - buffer_size = OC_MAX_APP_DATA_SIZE; + buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; } OC_DBG("creating new block-wise request buffer"); request_buffer = oc_blockwise_alloc_request_buffer( @@ -458,10 +459,10 @@ coap_receive(oc_message_t *msg) "sessions"); goto init_reset_message; } - uint32_t buffer_size = OC_MAX_APP_DATA_SIZE; + uint32_t buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; if (coap_get_header_size2(message, &buffer_size) && (buffer_size == 0)) { - buffer_size = OC_MAX_APP_DATA_SIZE; + buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; } request_buffer = oc_blockwise_alloc_request_buffer( @@ -514,10 +515,10 @@ coap_receive(oc_message_t *msg) oc_blockwise_free_request_buffer(request_buffer); request_buffer = NULL; } - uint32_t buffer_size = OC_MAX_APP_DATA_SIZE; + uint32_t buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; if (coap_get_header_size1(message, &buffer_size) && buffer_size == 0) { - buffer_size = OC_MAX_APP_DATA_SIZE; + buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; } request_buffer = oc_blockwise_alloc_request_buffer( href, href_len, &msg->endpoint, message->code, @@ -732,7 +733,7 @@ coap_receive(oc_message_t *msg) response_buffer = oc_blockwise_find_response_buffer_by_client_cb( &msg->endpoint, client_cb); if (!response_buffer) { - uint32_t buffer_size = OC_MAX_APP_DATA_SIZE; + uint32_t buffer_size = (uint32_t)OC_MAX_APP_DATA_SIZE; response_buffer = oc_blockwise_alloc_response_buffer( oc_string(client_cb->uri) + 1, oc_string_len(client_cb->uri) - 1, &msg->endpoint, client_cb->method, OC_BLOCKWISE_CLIENT, diff --git a/messaging/coap/separate.c b/messaging/coap/separate.c index d6119f1835..3e5d162a69 100644 --- a/messaging/coap/separate.c +++ b/messaging/coap/separate.c @@ -161,8 +161,9 @@ coap_separate_accept(const coap_packet_t *request, } void -coap_separate_resume(coap_packet_t *response, coap_separate_t *separate_store, - uint8_t code, uint16_t mid) +coap_separate_resume(coap_packet_t *response, + const coap_separate_t *separate_store, uint8_t code, + uint16_t mid) { #ifdef OC_TCP if ((separate_store->endpoint.flags & TCP) != 0) { diff --git a/messaging/coap/separate.h b/messaging/coap/separate.h index c0dff838c7..b073fb7de2 100644 --- a/messaging/coap/separate.h +++ b/messaging/coap/separate.h @@ -89,9 +89,11 @@ bool coap_separate_accept(const coap_packet_t *request, oc_separate_response_t *separate_response, const oc_endpoint_t *endpoint, int observe, uint16_t block2_size); + void coap_separate_resume(coap_packet_t *response, - coap_separate_t *separate_store, uint8_t code, + const coap_separate_t *separate_store, uint8_t code, uint16_t mid); + void coap_separate_clear(oc_separate_response_t *separate_response, coap_separate_t *separate_store); diff --git a/onboarding_tool/obtmain.c b/onboarding_tool/obtmain.c index f81669b53f..48a401fa1b 100644 --- a/onboarding_tool/obtmain.c +++ b/onboarding_tool/obtmain.c @@ -1174,8 +1174,7 @@ provision_role_wildcard_ace(void) device_handle_t *devices[MAX_NUM_DEVICES]; device_handle_t *device = (device_handle_t *)oc_list_head(owned_devices); - int i = 0, dev; - + int i = 0; OC_PRINTF("\nProvision role * ACE\nMy Devices:\n"); while (device != NULL) { devices[i] = device; @@ -1192,6 +1191,7 @@ provision_role_wildcard_ace(void) return; } + int dev; OC_PRINTF("\n\nSelect device for provisioning: "); SCANF("%d", &dev); if (dev < 0 || dev >= i) { 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/linux/ipadapter.c b/port/linux/ipadapter.c index 723f560b68..0e622862b2 100644 --- a/port/linux/ipadapter.c +++ b/port/linux/ipadapter.c @@ -184,10 +184,9 @@ static void remove_all_network_interface_cbs(void) { oc_network_interface_cb_t *cb_item = - oc_list_head(oc_network_interface_cb_list), - *next; + oc_list_head(oc_network_interface_cb_list); while (cb_item != NULL) { - next = cb_item->next; + oc_network_interface_cb_t *next = cb_item->next; oc_list_remove(oc_network_interface_cb_list, cb_item); oc_memb_free(&oc_network_interface_cb_s, cb_item); cb_item = next; 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_acl.c b/security/oc_acl.c index 6dac9a6a86..2f296186f2 100644 --- a/security/oc_acl.c +++ b/security/oc_acl.c @@ -231,10 +231,9 @@ oc_ace_get_permission(const oc_sec_ace_t *ace, const oc_resource_t *resource, oc_ace_wildcard_t wc = 0; if (!is_DCR) { if (resource->properties & OC_DISCOVERABLE) { + wc = OC_ACE_WC_ALL_SECURED; if (is_public) { - wc = OC_ACE_WC_ALL_PUBLIC | OC_ACE_WC_ALL_SECURED; - } else { - wc = OC_ACE_WC_ALL_SECURED; + wc |= OC_ACE_WC_ALL_PUBLIC; } } else { wc = OC_ACE_WC_ALL; @@ -556,8 +555,8 @@ oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, } if ((pstat->s == OC_DOS_RFPRO || pstat->s == OC_DOS_RFNOP || pstat->s == OC_DOS_SRESET) && - oc_string_len(resource->uri) == 14 && - memcmp(oc_string(resource->uri), "/oic/sec/roles", 14) == 0) { + oc_string_is_cstr_equal(&resource->uri, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { OC_DBG("oc_acl: peer has implicit access to /oic/sec/roles in RFPRO, " "RFNOP, SRESET"); return true; @@ -600,7 +599,8 @@ oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, } #ifdef OC_PKI else { - const oc_sec_cred_t *role_cred = peer ? oc_sec_get_roles(peer) : NULL; + const oc_sec_cred_t *role_cred = + peer != NULL ? oc_sec_roles_get(peer) : NULL; while (role_cred) { const oc_sec_cred_t *next = role_cred->next; uint32_t flags = 0; @@ -907,10 +907,9 @@ oc_sec_ace_update_res(oc_ace_subject_type_t type, static void oc_ace_free_resources(size_t device, oc_sec_ace_t **ace, const char *href) { - oc_ace_res_t *res = (oc_ace_res_t *)oc_list_head((*ace)->resources), - *next = NULL; + oc_ace_res_t *res = (oc_ace_res_t *)oc_list_head((*ace)->resources); while (res != NULL) { - next = res->next; + oc_ace_res_t *next = res->next; if (href == NULL || (oc_string_len(res->href) == strlen(href) && memcmp(href, oc_string(res->href), strlen(href)) == 0)) { 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..7a7c141ec7 --- /dev/null +++ b/security/oc_certs_generate.c @@ -0,0 +1,450 @@ +/**************************************************************************** + * + * 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(const oc_certs_generate_t *data, unsigned char *buffer, + size_t buffer_size) +{ + assert(data != NULL); + 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..018efd79a0 --- /dev/null +++ b/security/oc_certs_generate_internal.h @@ -0,0 +1,156 @@ +/**************************************************************************** + * + * 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 (cannot be NULL) + * @param[out] buffer output buffer to store the generated certificate (cannot + * be NULL) + * @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(const 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 56f7dedab5..8136d25083 100644 --- a/security/oc_cred.c +++ b/security/oc_cred.c @@ -18,6 +18,8 @@ #ifdef OC_SECURITY +#include "api/oc_helpers_internal.h" +#include "api/oc_core_res_internal.h" #include "oc_cred_internal.h" #include "oc_api.h" #include "oc_base64.h" @@ -36,7 +38,7 @@ #include "util/oc_list.h" #include "util/oc_macros_internal.h" #include "util/oc_memb.h" -#include +#include "util/oc_secure_string_internal.h" #ifdef OC_OSCORE #include "oc_oscore_context.h" @@ -47,20 +49,25 @@ #include #endif /* OC_PKI */ +#include +#include + OC_MEMB(g_creds, oc_sec_cred_t, OC_MAX_NUM_DEVICES *OC_MAX_NUM_SUBJECTS + 1); #define OXM_JUST_WORKS "oic.sec.doxm.jw" #define OXM_RANDOM_DEVICE_PIN "oic.sec.doxm.rdp" #define OXM_MANUFACTURER_CERTIFICATE "oic.sec.doxm.mfgcert" #ifdef OC_DYNAMIC_ALLOCATION -static oc_sec_creds_t *devices; -#else /* OC_DYNAMIC_ALLOCATION */ -static oc_sec_creds_t devices[OC_MAX_NUM_DEVICES]; -#endif /* !OC_DYNAMIC_ALLOCATION */ +static oc_sec_creds_t *g_devices = NULL; +#else /* !OC_DYNAMIC_ALLOCATION */ +static oc_sec_creds_t g_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 oc_string_view_t g_allowed_roles[] = { { + .data = "oic.role.owner", + .length = OC_CHAR_ARRAY_LEN("oic.role.owner"), +} }; #endif /* OC_PKI */ // https://openconnectivity.org/specs/OCF_Security_Specification_v2.2.5.pdf @@ -82,28 +89,28 @@ check_symmetric_key_length(size_t key_size) oc_sec_creds_t * oc_sec_get_creds(size_t device) { - return &devices[device]; + return &g_devices[device]; } void oc_sec_cred_init(void) { #ifdef OC_DYNAMIC_ALLOCATION - devices = + g_devices = (oc_sec_creds_t *)calloc(oc_core_get_num_devices(), sizeof(oc_sec_creds_t)); - if (!devices) { + if (g_devices == NULL) { oc_abort("Insufficient memory"); } #endif /* OC_DYNAMIC_ALLOCATION */ for (size_t i = 0; i < oc_core_get_num_devices(); i++) { - OC_LIST_STRUCT_INIT(&devices[i], creds); + OC_LIST_STRUCT_INIT(&g_devices[i], creds); } } oc_sec_cred_t * oc_sec_get_cred_by_credid(int credid, size_t device) { - oc_sec_cred_t *cred = oc_list_head(devices[device].creds); + oc_sec_cred_t *cred = oc_list_head(g_devices[device].creds); while (cred != NULL) { if (cred->credid == credid) { return cred; @@ -121,11 +128,11 @@ oc_sec_is_existing_cred(int credid, bool roles_resource, (void)client; if (!roles_resource) { - cred = (oc_sec_cred_t *)oc_list_head(devices[device].creds); + cred = (oc_sec_cred_t *)oc_list_head(g_devices[device].creds); } #ifdef OC_PKI else { - cred = oc_sec_get_roles(client); + cred = oc_sec_roles_get(client); } #endif /* OC_PKI */ while (cred != NULL) { @@ -188,7 +195,7 @@ oc_sec_find_role_cred(oc_sec_cred_t *start, const char *role, oc_sec_cred_t *creds = start; if (!creds) { /* Checking only the 0th logical device for Clients */ - creds = (oc_sec_cred_t *)oc_list_head(devices[0].creds); + creds = (oc_sec_cred_t *)oc_list_head(g_devices[0].creds); } size_t role_len = strlen(role); size_t authority_len = authority != NULL ? strlen(authority) : 0; @@ -223,7 +230,7 @@ get_new_credid(bool roles_resource, const oc_tls_peer_t *client, size_t device) static oc_sec_cred_t * oc_sec_remove_cred_from_device(oc_sec_cred_t *cred, size_t device) { - return oc_list_remove2(devices[device].creds, cred); + return oc_list_remove2(g_devices[device].creds, cred); } static oc_sec_cred_t * @@ -241,8 +248,8 @@ oc_sec_free_cred(oc_sec_cred_t *cred) { if (oc_string_len(cred->role.role) > 0) { #if defined(OC_PKI) && defined(OC_CLIENT) - oc_sec_remove_role_cred(oc_string(cred->role.role), - oc_string(cred->role.authority)); + oc_sec_role_cred_remove(oc_string_view2(&cred->role.role), + oc_string_view2(&cred->role.authority)); #endif /* OC_PKI && OC_CLIENT */ } oc_free_string(&cred->role.role); @@ -297,7 +304,7 @@ oc_sec_remove_cred_by_credid(int credid, size_t device) void oc_sec_cred_clear(size_t device, oc_sec_cred_filter_t filter, void *user_data) { - oc_sec_cred_t *cred = oc_list_head(devices[device].creds); + oc_sec_cred_t *cred = oc_list_head(g_devices[device].creds); while (cred != NULL) { oc_sec_cred_t *next = cred->next; if (filter == NULL || filter(cred, user_data)) { @@ -311,7 +318,7 @@ void oc_sec_cred_default(size_t device) { oc_sec_cred_clear(device, NULL, NULL); - memset(devices[device].rowneruuid.id, 0, OC_UUID_ID_SIZE); + memset(g_devices[device].rowneruuid.id, 0, OC_UUID_ID_SIZE); oc_sec_dump_cred(device); } @@ -322,8 +329,8 @@ oc_sec_cred_free(void) oc_sec_cred_clear(device, NULL, NULL); } #ifdef OC_DYNAMIC_ALLOCATION - if (devices) { - free(devices); + if (g_devices != NULL) { + free(g_devices); } #endif /* OC_DYNAMIC_ALLOCATION */ } @@ -334,7 +341,7 @@ oc_sec_find_creds_for_subject(oc_sec_cred_t *start, { oc_sec_cred_t *cred = start; if (!cred) { - cred = oc_list_head(devices[device].creds); + cred = oc_list_head(g_devices[device].creds); } while (cred != NULL) { if (oc_uuid_is_equal(cred->subjectuuid, *subjectuuid)) { @@ -354,7 +361,7 @@ oc_sec_find_cred(oc_sec_cred_t *start, const oc_uuid_t *subjectuuid, oc_sec_cred_t *cred = start; if (!cred) { - cred = oc_list_head(devices[device].creds); + cred = oc_list_head(g_devices[device].creds); } while (cred != NULL) { if (cred->credtype == credtype && @@ -373,8 +380,6 @@ oc_sec_cred_t * oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, size_t device) { - (void)credusage; - oc_sec_cred_t *cred = oc_memb_alloc(&g_creds); if (cred == NULL) { OC_WRN("insufficient memory to add new credential"); @@ -383,29 +388,32 @@ oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, oc_sec_credtype_t credtype, cred->credtype = credtype; #ifdef OC_PKI cred->credusage = credusage; +#else /* OC_PKI */ + (void)credusage; #endif /* OC_PKI */ memcpy(cred->subjectuuid.id, subjectuuid->id, OC_UUID_ID_SIZE); - oc_list_add(devices[device].creds, cred); + oc_list_add(g_devices[device].creds, cred); return cred; } #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].data, + g_allowed_roles[i].length, false)) { + return true; } } OC_ERR("oic.role.* roles assertion is prohibited"); - return -1; + return false; } - return 0; + return true; } static bool @@ -440,9 +448,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 && @@ -450,10 +460,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 */ @@ -468,13 +483,13 @@ oc_sec_is_equal_cred_data(oc_cred_data_t creddata, const uint8_t *data, } static bool -oc_sec_is_equal_cred_tag(const oc_string_t *credtag, const char *tag) +oc_sec_is_equal_cred_tag(const oc_string_t *credtag, oc_string_view_t tag) { size_t credtag_size = credtag->size; - size_t tag_size = tag != NULL ? strlen(tag) + 1 : 0; + size_t tag_size = tag.data != NULL ? tag.length + 1 : 0; return (credtag_size == tag_size) && - ((tag == NULL) || - (memcmp(oc_string(*credtag), tag, credtag_size) == 0)); + ((tag.data == NULL) || + (memcmp(oc_string(*credtag), tag.data, credtag_size) == 0)); } static bool @@ -502,7 +517,7 @@ oc_sec_is_duplicate_cred(const oc_sec_cred_t *cred, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, oc_uuid_t subject, size_t privatedata_size, const uint8_t *privatedata, size_t publicdata_size, const uint8_t *publicdata, - const char *tag) + oc_string_view_t tag) { if ((cred->credtype != credtype) || !oc_uuid_is_equal(cred->subjectuuid, subject) || @@ -554,28 +569,203 @@ oc_sec_get_valid_ecdsa_keypair(size_t device, size_t public_key_len, } #endif /* OC_PKI */ +static oc_sec_cred_t * +cred_allocate(size_t device, bool roles_resource, const oc_tls_peer_t *client, + oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, + const oc_uuid_t *subject) +{ +#ifdef OC_PKI + if (roles_resource) { + if (credusage == OC_CREDUSAGE_ROLE_CERT) { + return oc_sec_roles_add(client, device); + } + return NULL; + } +#else /* !OC_PKI */ + (void)roles_resource; + (void)client; +#endif /* OC_PKI */ + return oc_sec_allocate_cred(subject, credtype, credusage, device); +} + +static void +cred_deallocate(oc_sec_cred_t *cred, size_t device, bool roles_resource, + const oc_tls_peer_t *client) +{ +#ifdef OC_PKI + if (roles_resource) { + oc_sec_free_role(cred, client); + return; + } +#else /* !OC_PKI */ + (void)roles_resource; + (void)client; +#endif /* OC_PKI */ + oc_sec_remove_cred(cred, device); +} + +static int +cred_set_privatedata(oc_sec_cred_t *cred, const uint8_t *data, size_t data_size, + oc_sec_encoding_t encoding) +{ + if (cred->credtype == OC_CREDTYPE_PSK) { + if (encoding == OC_ENCODING_BASE64) { + if (data_size > 64) { + return -1; + } + uint8_t key[64]; + memcpy(key, data, data_size); + int key_size = oc_base64_decode(key, data_size); + if (key_size < 0 || !check_symmetric_key_length((size_t)key_size)) { + return -1; + } + oc_new_string(&cred->privatedata.data, (const char *)key, key_size); + cred->privatedata.encoding = OC_ENCODING_RAW; + return 0; + } + if (!check_symmetric_key_length(data_size)) { + return -1; + } + } + oc_new_string(&cred->privatedata.data, (const char *)data, data_size); + cred->privatedata.encoding = encoding; + return 0; +} + +typedef struct cred_create_t +{ + const oc_uuid_t *subject; + const oc_tls_peer_t *client; + size_t device; + int credid; + oc_sec_credtype_t credtype; + oc_sec_credusage_t credusage; + bool roles_resource; + oc_sec_encoded_data_t privatedata; + oc_string_view_t role; + oc_string_view_t authority; + oc_string_view_t tag; +#ifdef OC_PKI + oc_sec_encoded_data_t publicdata; + const oc_ecdsa_keypair_t *keypair; +#endif /* OC_PKI */ +} cred_create_t; + +static oc_sec_cred_t * +cred_create(const cred_create_t *create) +{ + oc_sec_cred_t *cred = + cred_allocate(create->device, create->roles_resource, create->client, + create->credtype, create->credusage, create->subject); + if (cred == NULL) { + return NULL; + } + +#ifdef OC_PKI + if (create->credusage == OC_CREDUSAGE_ROLE_CERT) { + if (oc_certs_parse_role_certificate(create->publicdata.data, + create->publicdata.size + 1, cred, + create->roles_resource) < 0) { + cred_deallocate(cred, create->device, create->roles_resource, + create->client); + return NULL; + } + + if (create->roles_resource && !check_role_assertion(cred)) { + oc_sec_free_role(cred, create->client); + return NULL; + } + } +#endif /* OC_PKI */ + + /* if a credid wasn't provided in the request, pick a suitable one */ + int credid = create->credid; + if (credid == -1) { + credid = + get_new_credid(create->roles_resource, create->client, create->device); + } + /* credid */ + cred->credid = credid; + /* credtype */ + cred->credtype = create->credtype; + /* privatedata */ + if (create->privatedata.data != NULL && create->privatedata.size > 0) { + if (cred_set_privatedata(cred, create->privatedata.data, + create->privatedata.size, + create->privatedata.encoding) != 0) { + cred_deallocate(cred, create->device, create->roles_resource, + create->client); + } + } else { +#ifdef OC_PKI + if (create->keypair != NULL) { + oc_new_string(&cred->privatedata.data, + (const char *)create->keypair->private_key, + create->keypair->private_key_size); + cred->privatedata.encoding = OC_ENCODING_RAW; + } +#endif /* OC_PKI */ + } + + /* roleid */ + if (!create->roles_resource && create->role.data != NULL) { + oc_new_string(&cred->role.role, create->role.data, create->role.length); + if (create->authority.data != NULL) { + oc_new_string(&cred->role.authority, create->authority.data, + create->authority.length); + } + } + +#ifdef OC_PKI + /* publicdata */ + if (create->publicdata.data != NULL && create->publicdata.size > 0) { + cred->publicdata.encoding = create->publicdata.encoding; + oc_new_string(&cred->publicdata.data, (const char *)create->publicdata.data, + create->publicdata.size); + } +#endif /* OC_PKI */ + + /* tag */ + if (create->tag.data != NULL) { + oc_new_string(&cred->tag, create->tag.data, create->tag.length); + } + 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, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, - const char *subjectuuid, - oc_sec_encoding_t privatedata_encoding, - size_t privatedata_size, const uint8_t *privatedata, - oc_sec_encoding_t publicdata_encoding, - size_t publicdata_size, const uint8_t *publicdata, - const char *role, const char *authority, const char *tag, + const char *subjectuuid, oc_sec_encoded_data_t privatedata, + oc_sec_encoded_data_t publicdata, oc_string_view_t role, + oc_string_view_t authority, oc_string_view_t tag, oc_sec_add_new_cred_data_t *new_cred_data) { - (void)publicdata_encoding; - (void)publicdata; - (void)publicdata_size; #ifdef OC_PKI oc_string_t public_key; memset(&public_key, 0, sizeof(oc_string_t)); size_t public_key_len = 0; if (credtype == OC_CREDTYPE_CERT) { int pk_len = oc_certs_parse_public_key_to_oc_string( - publicdata, publicdata_size + 1, &public_key); + publicdata.data, publicdata.size + 1, &public_key); if (pk_len < 0) { goto add_new_cred_error; } @@ -585,7 +775,7 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, if (roles_resource && (credtype != OC_CREDTYPE_CERT || !oc_sec_verify_role_cred(client, credusage, public_key_len, &public_key, - publicdata_size, publicdata))) { + publicdata.size, publicdata.data))) { goto add_new_cred_error; } #endif /* OC_PKI */ @@ -596,9 +786,9 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #ifdef OC_PKI const oc_ecdsa_keypair_t *kp = NULL; - if (credusage == OC_CREDUSAGE_IDENTITY_CERT && privatedata_size == 0) { + if (credusage == OC_CREDUSAGE_IDENTITY_CERT && privatedata.size == 0) { kp = oc_sec_get_valid_ecdsa_keypair(device, public_key_len, &public_key, - publicdata_size, publicdata); + publicdata.size, publicdata.data); if (!kp) { goto add_new_cred_error; } @@ -611,8 +801,8 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, if (!roles_resource) { /* skip duplicate cred, if one exists. */ if (oc_sec_is_duplicate_cred(existing, credtype, credusage, subject, - privatedata_size, privatedata, - publicdata_size, publicdata, tag)) { + privatedata.size, privatedata.data, + publicdata.size, publicdata.data, tag)) { #ifdef OC_PKI oc_free_string(&public_key); #endif /* OC_PKI */ @@ -633,8 +823,8 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #endif /* OC_PKI */ } - oc_sec_cred_t *cred = NULL; if (!roles_resource) { + oc_sec_cred_t *cred = NULL; do { cred = oc_sec_find_creds_for_subject(cred, &subject, device); @@ -652,15 +842,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, - 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 */ } @@ -670,11 +857,11 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, } #ifdef OC_PKI else { - oc_sec_cred_t *roles = oc_sec_get_roles(client); + oc_sec_cred_t *roles = oc_sec_roles_get(client); while (roles) { /* Trying to add a duplicate role credential, so ignore */ - if (oc_sec_is_equal_cred_data(roles->publicdata, publicdata, - publicdata_size) && + if (oc_sec_is_equal_cred_data(roles->publicdata, publicdata.data, + publicdata.size) && oc_sec_is_equal_cred_tag(&roles->tag, tag)) { oc_free_string(&public_key); return roles->credid; @@ -684,112 +871,31 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, } #endif /* OC_PKI */ - cred = NULL; + cred_create_t create = { + .subject = &subject, + .client = client, + .device = device, + .credid = credid, + .credtype = credtype, + .credusage = credusage, + .roles_resource = roles_resource, + .privatedata = privatedata, + .role = role, + .authority = authority, #ifdef OC_PKI - if (roles_resource && credusage == OC_CREDUSAGE_ROLE_CERT) { - cred = oc_sec_allocate_role(client, device); - } else if (!roles_resource) + .publicdata = publicdata, + .keypair = kp, #endif /* OC_PKI */ - { - cred = oc_sec_allocate_cred(&subject, credtype, credusage, device); - } - if (!cred) { + }; + const oc_sec_cred_t *cred = cred_create(&create); + if (cred == NULL) { goto add_new_cred_error; } -#ifdef OC_PKI - if (credusage == OC_CREDUSAGE_ROLE_CERT) { - if (oc_certs_parse_role_certificate(publicdata, publicdata_size + 1, cred, - roles_resource) < 0) { - if (roles_resource) { - oc_sec_free_role(cred, client); - } else { - oc_sec_remove_cred(cred, device); - } - goto add_new_cred_error; - } - - if (roles_resource && check_role_assertion(cred) < 0) { - oc_sec_free_role(cred, client); - goto add_new_cred_error; - } - } -#endif /* OC_PKI */ - - /* if a credid wasn't provided in the request, pick a suitable one */ - if (credid == -1) { - credid = get_new_credid(roles_resource, client, device); - } if (new_cred_data) { new_cred_data->created = true; } - /* credid */ - cred->credid = credid; - /* credtype */ - cred->credtype = credtype; - - /* privatedata */ - if (privatedata && privatedata_size > 0) { - if (credtype == OC_CREDTYPE_PSK && - privatedata_encoding == OC_ENCODING_BASE64) { - if (privatedata_size > 64) { - oc_sec_remove_cred(cred, device); - goto add_new_cred_error; - } - uint8_t key[64]; - memcpy(key, privatedata, privatedata_size); - int key_size = oc_base64_decode(key, privatedata_size); - if (key_size < 0 || !check_symmetric_key_length((size_t)key_size)) { - oc_sec_remove_cred(cred, device); - goto add_new_cred_error; - } - oc_new_string(&cred->privatedata.data, (const char *)key, key_size); - privatedata_encoding = OC_ENCODING_RAW; - } else { - if (credtype == OC_CREDTYPE_PSK && - !check_symmetric_key_length(privatedata_size)) { - oc_sec_remove_cred(cred, device); - goto add_new_cred_error; - } - oc_new_string(&cred->privatedata.data, (const char *)privatedata, - privatedata_size); - } - cred->privatedata.encoding = privatedata_encoding; - } -#ifdef OC_PKI - else if (kp) { - oc_new_string(&cred->privatedata.data, (const char *)kp->private_key, - kp->private_key_size); - cred->privatedata.encoding = OC_ENCODING_RAW; - } -#endif /* OC_PKI */ - - /* roleid */ - if (!roles_resource && role) { - oc_new_string(&cred->role.role, role, strlen(role)); - if (authority) { - oc_new_string(&cred->role.authority, authority, strlen(authority)); - } - } - -#ifdef OC_PKI - /* publicdata */ - if (publicdata && publicdata_size > 0) { - cred->publicdata.encoding = publicdata_encoding; - oc_new_string(&cred->publicdata.data, (const char *)publicdata, - publicdata_size); - } - - /* credusage */ - cred->credusage = credusage; -#endif /* OC_PKI */ - - /* tag */ - if (tag) { - oc_new_string(&cred->tag, tag, strlen(tag)); - } - #ifdef OC_PKI if (cred->credtype == OC_CREDTYPE_CERT) { if (cred->credusage == OC_CREDUSAGE_MFG_CERT || @@ -803,8 +909,8 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #if defined(OC_PKI) && defined(OC_CLIENT) if (!roles_resource && credusage == OC_CREDUSAGE_ROLE_CERT && oc_string_len(cred->role.role) > 0) { - oc_sec_add_role_cred(oc_string(cred->role.role), - oc_string(cred->role.authority)); + oc_sec_role_cred_add_or_get(oc_string_view2(&cred->role.role), + oc_string_view2(&cred->role.authority)); } #endif /* OC_PKI && OC_CLIENT */ } @@ -836,108 +942,76 @@ oc_cred_credtype_string(oc_sec_credtype_t credtype) } #ifdef OC_PKI -const char * -oc_cred_read_credusage(oc_sec_credusage_t credusage) + +oc_string_view_t +oc_cred_credusage_to_string(oc_sec_credusage_t credusage) { switch (credusage) { case OC_CREDUSAGE_TRUSTCA: - return OC_CREDUSAGE_TRUSTCA_STR; + return oc_string_view(OC_CREDUSAGE_TRUSTCA_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_TRUSTCA_STR)); case OC_CREDUSAGE_IDENTITY_CERT: - return OC_CREDUSAGE_IDENTITY_CERT_STR; + return oc_string_view(OC_CREDUSAGE_IDENTITY_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_IDENTITY_CERT_STR)); case OC_CREDUSAGE_ROLE_CERT: - return OC_CREDUSAGE_ROLE_CERT_STR; + return oc_string_view(OC_CREDUSAGE_ROLE_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_ROLE_CERT_STR)); case OC_CREDUSAGE_MFG_TRUSTCA: - return OC_CREDUSAGE_MFG_TRUSTCA_STR; + return oc_string_view(OC_CREDUSAGE_MFG_TRUSTCA_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_TRUSTCA_STR)); case OC_CREDUSAGE_MFG_CERT: - return OC_CREDUSAGE_MFG_CERT_STR; + return oc_string_view(OC_CREDUSAGE_MFG_CERT_STR, + OC_CHAR_ARRAY_LEN(OC_CREDUSAGE_MFG_CERT_STR)); default: break; } - return "None"; + return oc_string_view("None", OC_CHAR_ARRAY_LEN("None")); } -#endif /* OC_PKI */ const char * -oc_cred_read_encoding(oc_sec_encoding_t encoding) +oc_cred_read_credusage(oc_sec_credusage_t credusage) { - // TODO: use oc_string_view_t + oc_string_view_t cu = oc_cred_credusage_to_string(credusage); + return cu.data; +} +#endif /* OC_PKI */ +oc_string_view_t +oc_cred_encoding_to_string(oc_sec_encoding_t encoding) +{ switch (encoding) { case OC_ENCODING_BASE64: - return OC_ENCODING_BASE64_STR; + return oc_string_view(OC_ENCODING_BASE64_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_BASE64_STR)); case OC_ENCODING_RAW: - return OC_ENCODING_RAW_STR; + return oc_string_view(OC_ENCODING_RAW_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR)); #ifdef OC_PKI case OC_ENCODING_PEM: - return OC_ENCODING_PEM_STR; + return oc_string_view(OC_ENCODING_PEM_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_PEM_STR)); #endif /* OC_PKI */ case OC_ENCODING_HANDLE: - return OC_ENCODING_HANDLE_STR; + return oc_string_view(OC_ENCODING_HANDLE_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_HANDLE_STR)); default: break; } - return "Unknown"; + return oc_string_view("Unknown", OC_CHAR_ARRAY_LEN("Unknown")); } -#ifdef OC_PKI -static void -oc_sec_encode_roles(const oc_tls_peer_t *client, size_t device, - oc_interface_mask_t iface_mask) +const char * +oc_cred_read_encoding(oc_sec_encoding_t encoding) { - oc_sec_cred_t *cr = oc_sec_get_roles(client); - oc_rep_start_root_object(); - if (iface_mask & OC_IF_BASELINE) { - oc_process_baseline_interface( - oc_core_get_resource_by_index(OCF_SEC_ROLES, device)); - } - oc_rep_set_array(root, roles); - while (cr != NULL) { - oc_rep_object_array_start_item(roles); - /* credid */ - oc_rep_set_int(roles, credid, cr->credid); - /* credtype */ - oc_rep_set_int(roles, credtype, cr->credtype); - /* credusage */ - const char *credusage_string = oc_cred_read_credusage(cr->credusage); - size_t credusage_string_len = strlen(credusage_string); - if (credusage_string_len > 4) { - oc_rep_set_text_string_v1(roles, credusage, credusage_string, - credusage_string_len); - } - /* publicdata */ - if (oc_string_len(cr->publicdata.data) > 0) { - oc_rep_set_object(roles, publicdata); - if (cr->publicdata.encoding == OC_ENCODING_PEM) { - oc_rep_set_text_string_v1(publicdata, data, - oc_string(cr->publicdata.data), - oc_string_len(cr->publicdata.data)); - } else { - oc_rep_set_byte_string(publicdata, data, - oc_cast(cr->publicdata.data, const uint8_t), - oc_string_len(cr->publicdata.data)); - } - const char *encoding_string = - oc_cred_read_encoding(cr->publicdata.encoding); - size_t encoding_string_len = strlen(encoding_string); - if (encoding_string_len > 7) { - oc_rep_set_text_string_v1(publicdata, encoding, encoding_string, - encoding_string_len); - } - oc_rep_close_object(roles, publicdata); - } - oc_rep_object_array_end_item(roles); - cr = cr->next; - } - oc_rep_close_array(root, roles); - oc_rep_end_root_object(); + oc_string_view_t enc = oc_cred_encoding_to_string(encoding); + return enc.data; } -#endif /* OC_PKI */ void oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, bool to_storage) { - oc_sec_cred_t *cr = oc_list_head(devices[device].creds); + oc_sec_cred_t *cr = oc_list_head(g_devices[device].creds); char uuid[OC_UUID_LEN]; oc_rep_start_root_object(); if (to_storage || iface_mask & OC_IF_BASELINE) { @@ -956,7 +1030,8 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, oc_rep_set_text_string_v1(creds, subjectuuid, "*", 1); } else { oc_uuid_to_str(&cr->subjectuuid, uuid, OC_UUID_LEN); - oc_rep_set_text_string(creds, subjectuuid, uuid); + oc_rep_set_text_string_v1(creds, subjectuuid, uuid, + oc_strnlen(uuid, OC_UUID_LEN)); } /* roleid */ if ((to_storage || cr->credtype == OC_CREDTYPE_PSK) && @@ -991,15 +1066,14 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, oc_rep_set_text_string_v1(privatedata, data, "", 0); } } - const char *encoding_string = - oc_cred_read_encoding(cr->privatedata.encoding); - size_t encoding_string_len = strlen(encoding_string); - if (encoding_string_len <= 7) { - encoding_string = OC_ENCODING_RAW_STR; - encoding_string_len = OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR); + oc_string_view_t encoding_string = + oc_cred_encoding_to_string(cr->privatedata.encoding); + if (encoding_string.length <= 7) { + encoding_string = oc_string_view(OC_ENCODING_RAW_STR, + OC_CHAR_ARRAY_LEN(OC_ENCODING_RAW_STR)); } - oc_rep_set_text_string_v1(privatedata, encoding, encoding_string, - encoding_string_len); + oc_rep_set_text_string_v1(privatedata, encoding, encoding_string.data, + encoding_string.length); oc_rep_close_object(creds, privatedata); #ifdef OC_OSCORE /* oscore */ @@ -1031,11 +1105,11 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, #endif /* OC_OSCORE */ #ifdef OC_PKI /* credusage */ - const char *credusage_string = oc_cred_read_credusage(cr->credusage); - size_t credusage_string_len = strlen(credusage_string); - if (credusage_string_len > 4) { - oc_rep_set_text_string_v1(creds, credusage, credusage_string, - credusage_string_len); + oc_string_view_t credusage_string = + oc_cred_credusage_to_string(cr->credusage); + if (credusage_string.length > 4) { + oc_rep_set_text_string_v1(creds, credusage, credusage_string.data, + credusage_string.length); } /* publicdata */ if (oc_string_len(cr->publicdata.data) > 0) { @@ -1049,12 +1123,11 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, oc_cast(cr->publicdata.data, const uint8_t), oc_string_len(cr->publicdata.data)); } - const char *encoding_string = - oc_cred_read_encoding(cr->publicdata.encoding); - size_t encoding_string_len = strlen(encoding_string); - if (encoding_string_len > 7) { - oc_rep_set_text_string_v1(publicdata, encoding, encoding_string, - encoding_string_len); + oc_string_view_t encoding_string = + oc_cred_encoding_to_string(cr->publicdata.encoding); + if (encoding_string.length > 7) { + oc_rep_set_text_string_v1(publicdata, encoding, encoding_string.data, + encoding_string.length); } oc_rep_close_object(creds, publicdata); } @@ -1071,8 +1144,9 @@ oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, } oc_rep_close_array(root, creds); /* rowneruuid */ - oc_uuid_to_str(&devices[device].rowneruuid, uuid, OC_UUID_LEN); - oc_rep_set_text_string(root, rowneruuid, uuid); + oc_uuid_to_str(&g_devices[device].rowneruuid, uuid, OC_UUID_LEN); + oc_rep_set_text_string_v1(root, rowneruuid, uuid, + oc_strnlen(uuid, OC_UUID_LEN)); oc_rep_end_root_object(); } @@ -1327,7 +1401,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, case OC_REP_STRING: if (len == 10 && memcmp(oc_string(rep->name), "rowneruuid", 10) == 0) { oc_str_to_uuid(oc_string(rep->value.string), - &devices[device].rowneruuid); + &g_devices[device].rowneruuid); } break; /* creds */ @@ -1340,8 +1414,8 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, const oc_rep_t *cred = creds_array->value.object; int credid = -1; oc_sec_credtype_t credtype = 0; - const char *role = NULL; - const char *authority = NULL; + const oc_string_t *role = NULL; + const oc_string_t *authority = NULL; const char *subjectuuid = NULL; const char *privatedata = NULL; oc_sec_encoding_t privatedatatype = 0; @@ -1359,7 +1433,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, uint64_t ssn = 0; #endif /* OC_OSCORE */ bool owner_cred = false; - const char *tag = NULL; + const oc_string_t *tag = NULL; bool non_empty = false; while (cred != NULL) { non_empty = true; @@ -1388,7 +1462,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, #endif /* OC_PKI */ else if (len == 3 && memcmp(oc_string(cred->name), "tag", 3) == 0) { - tag = oc_string(cred->value.string); + tag = &cred->value.string; } break; /* publicdata, privatedata, roleid, oscore */ @@ -1457,10 +1531,10 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, len = oc_string_len(data->name); if (len == 4 && memcmp(oc_string(data->name), "role", 4) == 0) { - role = oc_string(data->value.string); + role = &data->value.string; } else if (len == 9 && memcmp(oc_string(data->name), "authority", 9) == 0) { - authority = oc_string(data->value.string); + authority = &data->value.string; } data = data->next; } @@ -1543,6 +1617,16 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, } #endif /* OC_OSCORE */ if (non_empty) { + oc_sec_encoded_data_t privdata = { (const uint8_t *)privatedata, + privatedata_size, + privatedatatype }; + oc_sec_encoded_data_t pubdata = { +#ifdef OC_PKI + (const uint8_t *)publicdata, publicdata_size, publicdatatype +#else /* !OC_PKI */ + /*data*/ NULL, /*size*/ 0, /*encoding*/ 0 +#endif /* OC_PKI */ + }; oc_sec_add_new_cred_data_t add_cred_data = { false, NULL }; credid = oc_sec_add_new_cred( device, roles_resource, client, credid, credtype, @@ -1551,14 +1635,8 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, #else /* OC_PKI */ 0, #endif /* !OC_PKI */ - subjectuuid, privatedatatype, privatedata_size, - (const uint8_t *)privatedata, -#ifdef OC_PKI - publicdatatype, publicdata_size, (const uint8_t *)publicdata, -#else /* OC_PKI */ - 0, 0, NULL, -#endif /* !OC_PKI */ - role, authority, tag, &add_cred_data); + subjectuuid, privdata, pubdata, oc_string_view2(role), + oc_string_view2(authority), oc_string_view2(tag), &add_cred_data); if (credid == -1) { return false; @@ -1619,30 +1697,6 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, return true; } -void -get_cred(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) -{ - (void)data; - bool roles_resource = false; -#ifdef OC_PKI - const oc_tls_peer_t *client = NULL; - if (oc_string_len(request->resource->uri) == 14 && - memcmp(oc_string(request->resource->uri), "/oic/sec/roles", 14) == 0) { - roles_resource = true; - } -#endif /* OC_PKI */ - if (!roles_resource) { - oc_sec_encode_cred(request->resource->device, iface_mask, false); - } -#ifdef OC_PKI - else { - client = oc_tls_get_peer(request->origin); - oc_sec_encode_roles(client, request->resource->device, iface_mask); - } -#endif /* OC_PKI */ - oc_send_response_with_callback(request, OC_STATUS_OK, true); -} - bool oc_cred_remove_subject(const char *subjectuuid, size_t device) { @@ -1653,10 +1707,11 @@ oc_cred_remove_subject(const char *subjectuuid, size_t device) } else { oc_str_to_uuid(subjectuuid, &_subjectuuid); } - oc_sec_cred_t *cred = oc_list_head(devices[device].creds), *next = 0; + oc_sec_cred_t *cred = oc_list_head(g_devices[device].creds); while (cred != NULL) { - next = cred->next; - if (memcmp(cred->subjectuuid.id, _subjectuuid.id, 16) == 0) { + oc_sec_cred_t *next = cred->next; + if (memcmp(cred->subjectuuid.id, _subjectuuid.id, + sizeof(_subjectuuid.id)) == 0) { oc_sec_remove_cred(cred, device); return true; } @@ -1665,72 +1720,6 @@ oc_cred_remove_subject(const char *subjectuuid, size_t device) return false; } -void -delete_cred(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) -{ - (void)iface_mask; - (void)data; - - bool roles_resource = false; - -#ifdef OC_PKI - const oc_tls_peer_t *client = NULL; - if (oc_string_len(request->resource->uri) == 14 && - memcmp(oc_string(request->resource->uri), "/oic/sec/roles", 14) == 0) { - client = oc_tls_get_peer(request->origin); - roles_resource = true; - } -#endif /* OC_PKI */ - - if (!roles_resource) { - const oc_sec_pstat_t *ps = oc_sec_get_pstat(request->resource->device); - if (ps->s == OC_DOS_RFNOP) { - OC_ERR("oc_cred: Cannot DELETE ACE in RFNOP"); - oc_send_response_with_callback(request, OC_STATUS_FORBIDDEN, true); - return; - } - } - - bool success = false; - const char *query_param = 0; - int ret = oc_get_query_value(request, "credid", &query_param); - int credid = 0; - if (ret != -1) { - credid = (int)strtoul(query_param, NULL, 10); - if (credid >= 0) { - if (!roles_resource) { - if (oc_sec_remove_cred_by_credid(credid, request->resource->device)) { - success = true; - } - } -#ifdef OC_PKI - else { - if (oc_sec_free_role_by_credid(credid, client) >= 0) { - success = true; - } - } -#endif /* OC_PKI */ - } - } else { - if (!roles_resource) { - oc_sec_cred_clear(request->resource->device, NULL, NULL); - } -#ifdef OC_PKI - else { - oc_sec_free_roles(client); - } -#endif /* OC_PKI */ - success = true; - } - - if (success) { - oc_send_response_with_callback(request, OC_STATUS_DELETED, true); - oc_sec_dump_cred(request->resource->device); - } else { - oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); - } -} - int oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, const oc_endpoint_t *endpoint, @@ -1741,10 +1730,8 @@ oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, const oc_tls_peer_t *client = NULL; #ifdef OC_PKI -#define OIC_SEC_ROLES "/oic/sec/roles" - if (oc_string_len(resource->uri) == strlen(OIC_SEC_ROLES) && - memcmp(oc_string(resource->uri), OIC_SEC_ROLES, strlen(OIC_SEC_ROLES)) == - 0) { + if (oc_string_is_cstr_equal(&resource->uri, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { roles_resource = true; client = oc_tls_get_peer(endpoint); } @@ -1762,7 +1749,7 @@ oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, #undef FIELD_ARRAY_SIZE if (!roles_resource && success && owner && - memcmp(owner->subjectuuid.id, devices[resource->device].rowneruuid.id, + memcmp(owner->subjectuuid.id, g_devices[resource->device].rowneruuid.id, uuid_size) == 0) { char deviceuuid[OC_UUID_LEN]; oc_uuid_to_str(&doxm->deviceuuid, deviceuuid, sizeof(deviceuuid)); @@ -1802,23 +1789,74 @@ oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, return 0; } -void -post_cred(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +cred_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { - (void)iface_mask; (void)data; + oc_sec_encode_cred(request->resource->device, iface_mask, false); + oc_send_response_with_callback(request, OC_STATUS_OK, true); +} - bool success = oc_sec_apply_cred(request->request_payload, request->resource, - request->origin, - /*on_apply_cred_cb*/ NULL, - /*on_apply_cred_data*/ NULL) == 0; +static void +cred_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; - if (!success) { + if (oc_sec_apply_cred(request->request_payload, request->resource, + request->origin, + /*on_apply_cred_cb*/ NULL, + /*on_apply_cred_data*/ NULL) != 0) { oc_send_response_with_callback(request, OC_STATUS_BAD_REQUEST, true); + return; + } + oc_send_response_with_callback(request, OC_STATUS_CHANGED, true); + oc_sec_dump_cred(request->resource->device); +} + +static void +cred_resource_delete(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; + + const oc_sec_pstat_t *ps = oc_sec_get_pstat(request->resource->device); + if (ps->s == OC_DOS_RFNOP) { + OC_ERR("oc_cred: Cannot DELETE ACE in RFNOP"); + oc_send_response_with_callback(request, OC_STATUS_FORBIDDEN, true); + return; + } + + 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_remove_cred_by_credid((int)credid, request->resource->device)) { + OC_ERR("oc_cred: cannot delete credential: invalid credid(%ld)", credid); + oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); + return; + } } else { - oc_send_response_with_callback(request, OC_STATUS_CHANGED, true); - oc_sec_dump_cred(request->resource->device); + oc_sec_cred_clear(request->resource->device, NULL, NULL); } + + oc_send_response_with_callback(request, OC_STATUS_DELETED, true); + oc_sec_dump_cred(request->resource->device); +} + +void +oc_sec_cred_create_resource(size_t device) +{ + oc_core_populate_resource( + OCF_SEC_CRED, device, "/oic/sec/cred", OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, + OC_DISCOVERABLE | OC_SECURE, cred_resource_get, /*put*/ NULL, + cred_resource_post, cred_resource_delete, 1, "oic.r.cred"); } #endif /* OC_SECURITY */ diff --git a/security/oc_cred_internal.h b/security/oc_cred_internal.h index e4e07dd757..824bd2ac4a 100644 --- a/security/oc_cred_internal.h +++ b/security/oc_cred_internal.h @@ -16,12 +16,15 @@ * ****************************************************************************/ -#ifndef OC_CRED_H -#define OC_CRED_H +#ifndef OC_CRED_INTERNAL_H +#define OC_CRED_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_cred.h" #include "oc_ri.h" #include "oc_uuid.h" +#include "util/oc_compiler.h" + #include #include @@ -53,14 +56,21 @@ typedef struct #endif /* OC_PKI */ -int oc_sec_add_new_cred( - size_t device, bool roles_resource, const struct oc_tls_peer_t *client, - int credid, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, - const char *subject, oc_sec_encoding_t privatedata_encoding, - size_t privatedata_size, const uint8_t *privatedata, - oc_sec_encoding_t publicdata_encoding, size_t publicdata_size, - const uint8_t *publicdata, const char *role, const char *authority, - const char *tag, oc_sec_add_new_cred_data_t *new_cred_data); +typedef struct +{ + const uint8_t *data; + size_t size; + oc_sec_encoding_t encoding; +} oc_sec_encoded_data_t; + +int oc_sec_add_new_cred(size_t device, bool roles_resource, + const struct oc_tls_peer_t *client, int credid, + oc_sec_credtype_t credtype, + oc_sec_credusage_t credusage, const char *subject, + oc_sec_encoded_data_t privatedata, + oc_sec_encoded_data_t publicdata, oc_string_view_t role, + oc_string_view_t authority, oc_string_view_t tag, + oc_sec_add_new_cred_data_t *new_cred_data); void oc_sec_cred_default(size_t device); void oc_sec_cred_init(void); @@ -73,6 +83,9 @@ bool oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, oc_sec_on_apply_cred_cb_t on_apply_cred_cb, void *on_apply_cred_data); +/** Convert encoding to oc_string_view_t */ +oc_string_view_t oc_cred_encoding_to_string(oc_sec_encoding_t encoding); + /** * @brief Parse cred encoding from string * @@ -80,16 +93,8 @@ bool oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, * @param str_len length of \p str * @return oc_sec_encoding_t parsed encoding */ -oc_sec_encoding_t oc_cred_encoding_from_string(const char *str, size_t str_len); - -/** - * @brief Parse cred usage from string - * - * @param str string (cannot be NULL) - * @param str_len length of \p str - * @return oc_sec_credusage_t parsed usage - */ -oc_sec_credusage_t oc_cred_usage_from_string(const char *str, size_t str_len); +oc_sec_encoding_t oc_cred_encoding_from_string(const char *str, size_t str_len) + OC_NONNULL(); /** * @brief Allocate and initialize a new credential and append it to global @@ -168,17 +173,32 @@ oc_sec_cred_t *oc_sec_find_cred(oc_sec_cred_t *start, oc_sec_cred_t *oc_sec_find_role_cred(oc_sec_cred_t *start, const char *role, const char *authority, const char *tag); -void put_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void post_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void get_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void delete_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); +/** + * @brief Create roles (/oic/sec/cred) resource for given device. + * + * @param device device index + */ +void oc_sec_cred_create_resource(size_t device); + +#ifdef OC_PKI + +/** @brief Convert credusage to oc_string_view_t */ +oc_string_view_t oc_cred_credusage_to_string(oc_sec_credusage_t credusage); + +/** + * @brief Parse cred usage from string + * + * @param str string (cannot be NULL) + * @param str_len length of \p str + * @return oc_sec_credusage_t parsed usage + */ +oc_sec_credusage_t oc_cred_usage_from_string(const char *str, size_t str_len) + OC_NONNULL(); + +#endif /* OC_PKI */ #ifdef __cplusplus } #endif -#endif /* OC_CRED_H */ +#endif /* OC_CRED_INTERNAL_H */ diff --git a/security/oc_obt.c b/security/oc_obt.c index 59a08dd34b..305f2c6e95 100644 --- a/security/oc_obt.c +++ b/security/oc_obt.c @@ -40,6 +40,7 @@ check oc_config.h and make sure OC_STORAGE is defined if OC_SECURITY is defined. #include "security/oc_csr_internal.h" #include "security/oc_keypair_internal.h" #include "security/oc_obt_internal.h" +#include "security/oc_roles_internal.h" #include "security/oc_security_internal.h" #include "security/oc_sdi_internal.h" #include "security/oc_tls_internal.h" @@ -303,73 +304,78 @@ oc_obt_dump_state(void) static void oc_obt_load_state(void) { - long ret = 0; - oc_rep_t *rep, *head; - uint8_t *buf = malloc(OC_MAX_APP_DATA_SIZE); if (!buf) { return; } - ret = oc_storage_read("obt_state", buf, OC_MAX_APP_DATA_SIZE); - if (ret > 0) { - struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; - oc_rep_set_pool(&rep_objects); - int err = oc_parse_rep(buf, ret, &rep); - head = rep; - if (err == 0) { - while (rep != NULL) { - switch (rep->type) { + long ret = oc_storage_read("obt_state", buf, OC_MAX_APP_DATA_SIZE); + if (ret <= 0) { + free(buf); + return; + } + + struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; + oc_rep_set_pool(&rep_objects); + oc_rep_t *rep = NULL; + int err = oc_parse_rep(buf, ret, &rep); + if (err != 0) { + oc_free_rep(rep); + free(buf); + return; + } + + oc_rep_t *head = rep; + while (rep != NULL) { + switch (rep->type) { #ifdef OC_PKI - case OC_REP_INT: + case OC_REP_INT: #define CREDID "credid" - if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(CREDID) && - memcmp(oc_string(rep->name), CREDID, OC_CHAR_ARRAY_LEN(CREDID)) == - 0) { - g_root_cert_credid = (int)rep->value.integer; - } - break; + if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(CREDID) && + memcmp(oc_string(rep->name), CREDID, OC_CHAR_ARRAY_LEN(CREDID)) == + 0) { + g_root_cert_credid = (int)rep->value.integer; + } + break; #endif /* OC_PKI */ #if defined(OC_PKI) || defined(OC_OSCORE) - case OC_REP_BYTE_STRING: + case OC_REP_BYTE_STRING: #ifdef OC_PKI #define PRIVATE_KEY "private_key" - if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(PRIVATE_KEY) && - memcmp(oc_string(rep->name), PRIVATE_KEY, - OC_CHAR_ARRAY_LEN(PRIVATE_KEY)) == 0) { - g_private_key_size = oc_string_len(rep->value.string); - memcpy(g_private_key, oc_string(rep->value.string), - g_private_key_size); - break; - } + if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(PRIVATE_KEY) && + memcmp(oc_string(rep->name), PRIVATE_KEY, + OC_CHAR_ARRAY_LEN(PRIVATE_KEY)) == 0) { + g_private_key_size = oc_string_len(rep->value.string); + memcpy(g_private_key, oc_string(rep->value.string), g_private_key_size); + break; + } #endif /* OC_PKI */ #ifdef OC_OSCORE #define GROUP_ID "groupid" - if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(GROUP_ID) && - memcmp(oc_string(rep->name), GROUP_ID, - OC_CHAR_ARRAY_LEN(GROUP_ID)) == 0) { - memcpy(g_groupid, oc_string(rep->value.string), OSCORE_CTXID_LEN); - break; - } + if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(GROUP_ID) && + memcmp(oc_string(rep->name), GROUP_ID, OC_CHAR_ARRAY_LEN(GROUP_ID)) == + 0) { + memcpy(g_groupid, oc_string(rep->value.string), OSCORE_CTXID_LEN); + break; + } #define GROUP_SECRET "group_secret" - if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(GROUP_SECRET) && - memcmp(oc_string(rep->name), GROUP_SECRET, - OC_CHAR_ARRAY_LEN(GROUP_SECRET)) == 0) { - memcpy(g_group_secret, oc_string(rep->value.string), - OSCORE_MASTER_SECRET_LEN); - break; - } + if (oc_string_len(rep->name) == OC_CHAR_ARRAY_LEN(GROUP_SECRET) && + memcmp(oc_string(rep->name), GROUP_SECRET, + OC_CHAR_ARRAY_LEN(GROUP_SECRET)) == 0) { + memcpy(g_group_secret, oc_string(rep->value.string), + OSCORE_MASTER_SECRET_LEN); + break; + } #endif /* OC_OSCORE */ - break; + break; #endif /* OC_PKI || OC_OSCORE */ - default: - break; - } - rep = rep->next; - } + default: + break; } - oc_free_rep(head); + rep = rep->next; } + + oc_free_rep(head); free(buf); } #endif /* OC_PKI || OC_OSCORE */ @@ -1668,7 +1674,6 @@ free_trustanchor_state(oc_trustanchor_ctx_t *p, int status) oc_tls_close_connection(ep); p->cb.cb(status, p->cb.data); - // p->cb.cb(p->cb.data); if (p->switch_dos) { free_switch_dos_state(p->switch_dos); @@ -1695,9 +1700,9 @@ oc_obt_add_roleid(oc_role_t *roles, const char *role, const char *authority) void oc_obt_free_roleid(oc_role_t *roles) { - oc_role_t *r = roles, *next; + oc_role_t *r = roles; while (r) { - next = r->next; + oc_role_t *next = r->next; oc_free_string(&r->role); oc_free_string(&r->authority); oc_memb_free(&oc_roles, r); @@ -1778,8 +1783,8 @@ device_cred(oc_client_response_t *data) oc_rep_set_array(aclist2, resources); oc_rep_object_array_start_item(resources); - oc_rep_set_text_string_v1(resources, href, "/oic/sec/roles", - OC_CHAR_ARRAY_LEN("/oic/sec/roles")); + oc_rep_set_text_string_v1(resources, href, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI)); oc_rep_object_array_end_item(resources); oc_rep_close_array(aclist2, resources); @@ -2691,9 +2696,9 @@ oc_obt_provision_ace(const oc_uuid_t *uuid, oc_sec_ace_t *ace, void oc_obt_free_creds(oc_sec_creds_t *creds) { - oc_sec_cred_t *cred = oc_list_head(creds->creds), *next; + oc_sec_cred_t *cred = oc_list_head(creds->creds); while (cred != NULL) { - next = cred->next; + oc_sec_cred_t *next = cred->next; oc_free_string(&cred->role.role); oc_free_string(&cred->role.authority); oc_free_string(&cred->privatedata.data); @@ -3191,9 +3196,9 @@ decode_acl(oc_rep_t *rep, oc_sec_acl_t *acl) void oc_obt_free_acl(oc_sec_acl_t *acl) { - oc_sec_ace_t *ace = (oc_sec_ace_t *)oc_list_pop(acl->subjects), *next; + oc_sec_ace_t *ace = (oc_sec_ace_t *)oc_list_pop(acl->subjects); while (ace) { - next = ace->next; + oc_sec_ace_t *next = ace->next; oc_obt_free_ace(ace); ace = next; } diff --git a/security/oc_obt_certs.c b/security/oc_obt_certs.c index 3bfd55de07..10fe804ffc 100644 --- a/security/oc_obt_certs.c +++ b/security/oc_obt_certs.c @@ -24,184 +24,66 @@ #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 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(&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) { return -1; } + oc_sec_encoded_data_t privatedata = { NULL, 0, 0 }; + oc_sec_encoded_data_t publicdata = { + cert_pem, oc_strnlen((const char *)cert_pem, sizeof(cert_pem)), + OC_ENCODING_PEM + }; int ret = oc_sec_add_new_cred( - device, false, NULL, -1, OC_CREDTYPE_CERT, OC_CREDUSAGE_TRUSTCA, "*", 0, 0, - NULL, OC_ENCODING_PEM, oc_strnlen((const char *)cert_pem, sizeof(cert_pem)), - cert_pem, NULL, NULL, NULL, NULL); + device, false, NULL, -1, OC_CREDTYPE_CERT, OC_CREDUSAGE_TRUSTCA, "*", + privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), + oc_string_view2(NULL), NULL); if (ret == -1) { OC_ERR("could not write root cert into /oic/sec/cred"); @@ -217,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 */ @@ -346,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 */ @@ -618,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 role_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(&role_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_obt_otm_cert.c b/security/oc_obt_otm_cert.c index 62c6f08998..77f59aa93a 100644 --- a/security/oc_obt_otm_cert.c +++ b/security/oc_obt_otm_cert.c @@ -359,10 +359,13 @@ obt_cert_9(oc_client_response_t *data) goto err_obt_cert_9; } + oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), + OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - suuid, OC_ENCODING_RAW, OC_ARRAY_SIZE(key), key, 0, 0, - NULL, NULL, NULL, NULL, NULL); + suuid, privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (credid == -1) { goto err_obt_cert_9; diff --git a/security/oc_obt_otm_justworks.c b/security/oc_obt_otm_justworks.c index 58eb7ef96d..1fe3a6f988 100644 --- a/security/oc_obt_otm_justworks.c +++ b/security/oc_obt_otm_justworks.c @@ -358,10 +358,13 @@ obt_jw_9(oc_client_response_t *data) goto err_obt_jw_9; } + oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), + OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - suuid, OC_ENCODING_RAW, OC_ARRAY_SIZE(key), key, 0, 0, - NULL, NULL, NULL, NULL, NULL); + suuid, privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (credid == -1) { goto err_obt_jw_9; diff --git a/security/oc_obt_otm_randompin.c b/security/oc_obt_otm_randompin.c index 5b4166fd9a..93e774fa5e 100644 --- a/security/oc_obt_otm_randompin.c +++ b/security/oc_obt_otm_randompin.c @@ -359,10 +359,13 @@ obt_rdp_7(oc_client_response_t *data) goto err_obt_rdp_7; } + oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), + OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - suuid, OC_ENCODING_RAW, OC_ARRAY_SIZE(key), key, 0, 0, - NULL, NULL, NULL, NULL, NULL); + suuid, privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (credid == -1) { goto err_obt_rdp_7; @@ -647,10 +650,13 @@ oc_obt_perform_random_pin_otm(const oc_uuid_t *uuid, const unsigned char *pin, * TLS layer. */ - int credid = - oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, - subjectuuid, OC_ENCODING_RAW, OC_ARRAY_SIZE(key), key, - 0, 0, NULL, NULL, NULL, NULL, NULL); + oc_sec_encoded_data_t privatedata = { key, OC_ARRAY_SIZE(key), + OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; + int credid = oc_sec_add_new_cred( + 0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, subjectuuid, + privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), + oc_string_view2(NULL), NULL); if (credid == -1) { oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP); diff --git a/security/oc_oscore_context.c b/security/oc_oscore_context.c index bcab49cd7e..1ebc3e12fc 100644 --- a/security/oc_oscore_context.c +++ b/security/oc_oscore_context.c @@ -247,8 +247,6 @@ oc_oscore_context_derive_param(const uint8_t *id, uint8_t id_len, uint8_t salt_len, uint8_t *param, uint8_t param_len) { - uint8_t info[OSCORE_INFO_MAX_LEN]; - CborEncoder e, a; CborError err = CborNoError; /* From RFC 8613: Section 3.2.1: @@ -260,7 +258,10 @@ oc_oscore_context_derive_param(const uint8_t *id, uint8_t id_len, L : uint, ] */ + CborEncoder e; + uint8_t info[OSCORE_INFO_MAX_LEN]; cbor_encoder_init(&e, info, OSCORE_INFO_MAX_LEN, 0); + CborEncoder a; /* Array of 5 elements */ err |= cbor_encoder_create_array(&e, &a, 5); /* Sender ID, Recipient ID or empty string for Common IV */ diff --git a/security/oc_oscore_crypto.c b/security/oc_oscore_crypto.c index 2599ac187a..1458f3dc7c 100644 --- a/security/oc_oscore_crypto.c +++ b/security/oc_oscore_crypto.c @@ -193,9 +193,6 @@ int oc_oscore_compose_AAD(const uint8_t *kid, uint8_t kid_len, const uint8_t *piv, uint8_t piv_len, uint8_t *AAD, uint8_t *AAD_len) { - uint8_t aad_array[OSCORE_AAD_MAX_LEN]; - - CborEncoder e, a, alg; CborError err = CborNoError; /* Compose aad_array... From RFC 8613 Section 5.4: @@ -208,12 +205,16 @@ oc_oscore_compose_AAD(const uint8_t *kid, uint8_t kid_len, const uint8_t *piv, options : bstr, ] */ + CborEncoder e; + uint8_t aad_array[OSCORE_AAD_MAX_LEN]; cbor_encoder_init(&e, aad_array, OSCORE_AAD_MAX_LEN, 0); /* Array of 5 elements */ + CborEncoder a; err |= cbor_encoder_create_array(&e, &a, 5); /* oscore_version: 1 */ err |= cbor_encode_uint(&a, 0x01); /* algorithms: contains only alg_aead (10) */ + CborEncoder alg; err |= cbor_encoder_create_array(&a, &alg, 1); err |= cbor_encode_int(&alg, 10); err |= cbor_encoder_close_container(&a, &alg); diff --git a/security/oc_oscore_engine.c b/security/oc_oscore_engine.c index cc6a484e0d..fb580ff0ab 100644 --- a/security/oc_oscore_engine.c +++ b/security/oc_oscore_engine.c @@ -397,13 +397,10 @@ oc_oscore_send_multicast_message(oc_message_t *message) } OC_DBG("### parsed CoAP message ###"); - - uint8_t piv[OSCORE_PIV_LEN], piv_len = 0, kid[OSCORE_CTXID_LEN], - kid_len = 0, nonce[OSCORE_AEAD_NONCE_LEN], - AAD[OSCORE_AAD_MAX_LEN], AAD_len = 0; - OC_DBG("### protecting multicast request ###"); /* Use context->SSN as Partial IV */ + uint8_t piv[OSCORE_PIV_LEN]; + uint8_t piv_len = 0; oscore_store_piv(oscore_ctx->ssn, piv, &piv_len); OC_DBG("---using SSN as Partial IV: %" PRIu64, oscore_ctx->ssn); OC_LOGbytes(piv, piv_len); @@ -411,10 +408,13 @@ oc_oscore_send_multicast_message(oc_message_t *message) oscore_ctx->ssn++; /* Use context-sendid as kid */ + uint8_t kid[OSCORE_CTXID_LEN]; + uint8_t kid_len = 0; memcpy(kid, oscore_ctx->sendid, oscore_ctx->sendid_len); kid_len = oscore_ctx->sendid_len; /* Compute nonce using partial IV and context->sendid */ + uint8_t nonce[OSCORE_AEAD_NONCE_LEN]; oc_oscore_AEAD_nonce(oscore_ctx->sendid, oscore_ctx->sendid_len, piv, piv_len, oscore_ctx->commoniv, nonce, OSCORE_AEAD_NONCE_LEN); @@ -423,6 +423,8 @@ oc_oscore_send_multicast_message(oc_message_t *message) OC_LOGbytes(nonce, OSCORE_AEAD_NONCE_LEN); /* Compose AAD using partial IV and context->sendid */ + uint8_t AAD[OSCORE_AAD_MAX_LEN]; + uint8_t AAD_len = 0; oc_oscore_compose_AAD(oscore_ctx->sendid, oscore_ctx->sendid_len, piv, piv_len, AAD, &AAD_len); OC_DBG("---composed AAD using Partial IV (SSN) and Sender ID"); @@ -598,11 +600,13 @@ oc_oscore_send_message(oc_message_t *msg) } OC_DBG("### parsed CoAP message ###"); - - uint8_t piv[OSCORE_PIV_LEN], piv_len = 0, kid[OSCORE_CTXID_LEN], - kid_len = 0, nonce[OSCORE_AEAD_NONCE_LEN], - AAD[OSCORE_AAD_MAX_LEN], AAD_len = 0; - + uint8_t piv[OSCORE_PIV_LEN]; + uint8_t piv_len = 0; + uint8_t kid[OSCORE_CTXID_LEN]; + uint8_t kid_len = 0; + uint8_t nonce[OSCORE_AEAD_NONCE_LEN]; + uint8_t AAD[OSCORE_AAD_MAX_LEN]; + uint8_t AAD_len = 0; /* If CoAP message is request */ if ((coap_pkt->code >= OC_GET && coap_pkt->code <= OC_DELETE) #ifdef OC_TCP diff --git a/security/oc_pki.c b/security/oc_pki.c index 8861c5c25b..baaa774e25 100644 --- a/security/oc_pki.c +++ b/security/oc_pki.c @@ -71,7 +71,7 @@ pki_add_intermediate_cert(size_t device, int credid, const unsigned char *cert, } OC_DBG("parsed intermediate CA cert"); - mbedtls_x509_crt id_cert_chain, *id_cert; + mbedtls_x509_crt id_cert_chain; mbedtls_x509_crt_init(&id_cert_chain); /* Parse the identity cert chain */ @@ -87,7 +87,7 @@ pki_add_intermediate_cert(size_t device, int credid, const unsigned char *cert, } OC_DBG("parsed identity cert chain"); - id_cert = &id_cert_chain; + mbedtls_x509_crt *id_cert = &id_cert_chain; for (; id_cert != NULL; id_cert = id_cert->next) { /* If this intermediate cert is already on the chain, return */ if (id_cert->raw.len == int_ca.raw.len && @@ -144,7 +144,6 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, { OC_DBG("attempting to add an identity certificate chain"); - size_t c_size = cert_size, k_size = key_size; mbedtls_pk_context pkey; mbedtls_pk_init(&pkey); @@ -152,9 +151,13 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, OC_ERR("provided cert is not in PEM format"); return -1; } + + size_t c_size = cert_size; if (cert[cert_size - 1] != '\0') { c_size += 1; } + + size_t k_size = key_size; if (oc_certs_is_PEM(key, key_size)) { if (key[key_size - 1] != '\0') { k_size += 1; @@ -184,7 +187,7 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, size_t private_key_size = ret; - mbedtls_x509_crt cert1, cert2; + mbedtls_x509_crt cert1; mbedtls_x509_crt_init(&cert1); /* Parse identity cert chain */ @@ -212,6 +215,7 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, if (c->credusage != credusage) { continue; } + mbedtls_x509_crt cert2; mbedtls_x509_crt_init(&cert2); ret = @@ -236,10 +240,13 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, mbedtls_x509_crt_free(&cert1); + oc_sec_encoded_data_t privatedata = { privkbuf + (200 - private_key_size), + private_key_size, OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { cert, c_size - 1, OC_ENCODING_PEM }; int credid = oc_sec_add_new_cred( device, false, NULL, -1, OC_CREDTYPE_CERT, credusage, subjectuuid, - OC_ENCODING_RAW, private_key_size, privkbuf + (200 - private_key_size), - OC_ENCODING_PEM, c_size - 1, cert, NULL, NULL, NULL, NULL); + privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), + oc_string_view2(NULL), NULL); if (credid != -1) { OC_DBG("added new identity cert(credid=%d) chain to /oic/sec/cred", credid); @@ -280,19 +287,17 @@ pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, oc_sec_credusage_t credusage) { OC_DBG("attempting to add a trust anchor"); - - mbedtls_x509_crt cert1, cert2; - mbedtls_x509_crt_init(&cert1); - size_t c_size = cert_size; - /* Parse root cert */ if (!oc_certs_is_PEM(cert, cert_size)) { OC_ERR("provided cert is not in PEM format"); return -1; } + size_t c_size = cert_size; if (cert[cert_size - 1] != '\0') { c_size += 1; } + mbedtls_x509_crt cert1; + mbedtls_x509_crt_init(&cert1); int ret = mbedtls_x509_crt_parse(&cert1, cert, c_size); if (ret < 0) { OC_ERR("could not parse the provided trust anchor: %d", ret); @@ -307,6 +312,7 @@ pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, if (c->credusage != credusage) { continue; } + mbedtls_x509_crt cert2; mbedtls_x509_crt_init(&cert2); ret = mbedtls_x509_crt_parse(&cert2, oc_cast(c->publicdata.data, unsigned char), @@ -336,9 +342,12 @@ pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, OC_DBG("adding a new trust anchor entry to /oic/sec/cred"); - ret = oc_sec_add_new_cred(device, false, NULL, -1, OC_CREDTYPE_CERT, - credusage, "*", 0, 0, NULL, OC_ENCODING_PEM, - c_size - 1, cert, NULL, NULL, NULL, NULL); + oc_sec_encoded_data_t privatedata = { NULL, 0, 0 }; + oc_sec_encoded_data_t publicdata = { cert, c_size - 1, OC_ENCODING_PEM }; + ret = + oc_sec_add_new_cred(device, false, NULL, -1, OC_CREDTYPE_CERT, credusage, + "*", privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (ret != -1) { OC_DBG("added new trust anchor entry to /oic/sec/cred"); oc_sec_dump_cred(device); diff --git a/security/oc_roles.c b/security/oc_roles.c index 8527dfb1c1..e3076b126d 100644 --- a/security/oc_roles.c +++ b/security/oc_roles.c @@ -16,81 +16,107 @@ * ****************************************************************************/ -#ifdef OC_SECURITY -#ifdef OC_PKI +#include "oc_config.h" -#include "oc_roles_internal.h" +#if defined(OC_SECURITY) && defined(OC_PKI) + +#include "api/oc_core_res_internal.h" +#include "api/oc_helpers_internal.h" #include "port/oc_log_internal.h" -#include "security/oc_tls_internal.h" +#include "oc_roles_internal.h" +#include "oc_tls_internal.h" #include -#define OC_ROLES_NUM_ROLE_CREDS (2) -#define OC_ROLES_NUM_ROLES (2) +#include +#include #ifdef OC_CLIENT -OC_MEMB(role_creds_s, oc_role_t, OC_ROLES_NUM_ROLE_CREDS); -OC_LIST(role_creds); -oc_role_t * -oc_sec_get_role_creds(void) -{ - return oc_list_head(role_creds); -} +OC_MEMB(g_role_cred_s, oc_role_t, OC_ROLES_NUM_ROLE_CREDS); +OC_LIST(g_role_creds); static oc_role_t * -allocate_role_cred(const char *role, const char *authority) +role_cred_allocate(oc_string_view_t role, oc_string_view_t authority) { - oc_role_t *role_cred = (oc_role_t *)oc_memb_alloc(&role_creds_s); - if (role) { - oc_new_string(&role_cred->role, role, strlen(role)); - oc_new_string(&role_cred->authority, authority, strlen(authority)); - oc_list_add(role_creds, role_cred); + if (role.data == NULL || authority.data == NULL) { + OC_ERR("invalid input"); + return NULL; + } + oc_role_t *role_cred = (oc_role_t *)oc_memb_alloc(&g_role_cred_s); + if (role_cred == NULL) { + OC_ERR("failed to allocate role cred"); + return NULL; } + oc_new_string(&role_cred->role, role.data, role.length); + oc_new_string(&role_cred->authority, authority.data, authority.length); + oc_list_add(g_role_creds, role_cred); return role_cred; } static oc_role_t * -find_role_cred(const char *role, const char *authority) -{ - oc_role_t *role_cred = (oc_role_t *)oc_list_head(role_creds); - size_t role_len = strlen(role); - size_t authority_len = (authority ? strlen(authority) : 0); - - while (role_cred) { - if ((oc_string_len(role_cred->role) == role_len) && - (memcmp(oc_string(role_cred->role), role, role_len) == 0)) { - if (authority && (oc_string_len(role_cred->authority) == authority_len) && - (memcmp(oc_string(role_cred->authority), authority, authority_len) == - 0)) { - return role_cred; - } +role_cred_find(oc_string_view_t role, oc_string_view_t authority) +{ + oc_role_t *role_cred = (oc_role_t *)oc_list_head(g_role_creds); + while (role_cred != NULL) { + if ((role.data != NULL && + oc_string_view_is_equal(oc_string_view2(&role_cred->role), role)) && + (authority.data != NULL && + oc_string_view_is_equal(oc_string_view2(&role_cred->authority), + authority))) { + return role_cred; } role_cred = role_cred->next; } + return NULL; +} +oc_role_t * +oc_sec_role_cred_add_or_get(oc_string_view_t role, oc_string_view_t authority) +{ + oc_role_t *role_cred = role_cred_find(role, authority); + if (!role_cred) { + role_cred = role_cred_allocate(role, authority); + } return role_cred; } -void -oc_sec_remove_role_cred(const char *role, const char *authority) +static void +role_cred_free(oc_role_t *role_cred) +{ + oc_free_string(&role_cred->authority); + oc_free_string(&role_cred->role); + oc_memb_free(&g_role_cred_s, role_cred); +} + +bool +oc_sec_role_cred_remove(oc_string_view_t role, oc_string_view_t authority) { - oc_role_t *role_cred = find_role_cred(role, authority); - if (role_cred) { - oc_list_remove(role_creds, role_cred); - oc_memb_free(&role_creds_s, role_cred); + oc_role_t *role_cred = role_cred_find(role, authority); + if (role_cred != NULL) { + oc_list_remove(g_role_creds, role_cred); + role_cred_free(role_cred); + return true; } + return false; } oc_role_t * -oc_sec_add_role_cred(const char *role, const char *authority) +oc_sec_role_creds_get(void) { - oc_role_t *role_cred = find_role_cred(role, authority); - if (!role_cred) { - role_cred = allocate_role_cred(role, authority); + return oc_list_head(g_role_creds); +} + +void +oc_sec_role_creds_free(void) +{ + oc_role_t *role_cred = (oc_role_t *)oc_list_pop(g_role_creds); + while (role_cred != NULL) { + role_cred_free(role_cred); + role_cred = (oc_role_t *)oc_list_pop(g_role_creds); } - return role_cred; } + #endif /* OC_CLIENT */ typedef struct oc_sec_roles_t @@ -101,151 +127,279 @@ typedef struct oc_sec_roles_t size_t device; } oc_sec_roles_t; -OC_MEMB(x509_crt_s, mbedtls_x509_crt, OC_ROLES_NUM_ROLES); -OC_MEMB(roles_s, oc_sec_cred_t, OC_ROLES_NUM_ROLES); -OC_MEMB(clients_s, oc_sec_roles_t, OC_MAX_NUM_DEVICES); -OC_LIST(clients); +OC_MEMB(g_x509_crt_s, mbedtls_x509_crt, OCF_SEC_ROLES_MAX_NUM); +OC_MEMB(g_roles_s, oc_sec_cred_t, OCF_SEC_ROLES_MAX_NUM); +OC_MEMB(g_clients_s, oc_sec_roles_t, OC_MAX_NUM_DEVICES); +OC_LIST(g_clients); static oc_sec_roles_t * -get_roles_for_client(const oc_tls_peer_t *client) +roles_get_for_client(const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(clients); - while (roles) { + oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(g_clients); + while (roles != NULL) { if (roles->client == client) { return roles; } roles = roles->next; } - return roles; + return NULL; } static oc_sec_roles_t * -allocate_roles_for_client(const oc_tls_peer_t *client, size_t device) +roles_add_for_client(const oc_tls_peer_t *client, size_t device) { - oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_memb_alloc(&clients_s); - if (!roles) { + oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_memb_alloc(&g_clients_s); + if (roles == NULL) { + OC_ERR("insufficient memory to allocate roles"); return NULL; } roles->device = device; roles->client = client; OC_LIST_STRUCT_INIT(roles, roles); - oc_list_add(clients, roles); + oc_list_add(g_clients, roles); return roles; } +static oc_sec_cred_t * +role_add(oc_sec_roles_t *roles) +{ + oc_sec_cred_t *role = (oc_sec_cred_t *)oc_memb_alloc(&g_roles_s); + if (role == NULL) { + OC_ERR("insufficient memory to allocate role"); + return NULL; + } + role->ctx = oc_memb_alloc(&g_x509_crt_s); + if (role->ctx == NULL) { + OC_ERR("insufficient memory to allocate role context"); + oc_memb_free(&g_roles_s, role); + return NULL; + } + mbedtls_x509_crt_init(role->ctx); + oc_list_add(roles->roles, role); + return role; +} + oc_sec_cred_t * -oc_sec_allocate_role(const oc_tls_peer_t *client, size_t device) -{ - oc_sec_roles_t *roles = get_roles_for_client(client); - if (!roles) { - roles = allocate_roles_for_client(client, device); - } - if (roles) { - oc_sec_cred_t *role = (oc_sec_cred_t *)oc_memb_alloc(&roles_s); - if (role) { - role->ctx = oc_memb_alloc(&x509_crt_s); - if (role->ctx) { - mbedtls_x509_crt_init(role->ctx); - oc_list_add(roles->roles, role); - return role; - } - oc_sec_free_role(role, client); - } +oc_sec_roles_add(const oc_tls_peer_t *client, size_t device) +{ + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles == NULL) { + roles = roles_add_for_client(client, device); } - return NULL; + if (roles == NULL) { + return NULL; + } + return role_add(roles); } oc_sec_cred_t * -oc_sec_get_roles(const oc_tls_peer_t *client) +oc_sec_roles_get(const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = get_roles_for_client(client); - if (roles) { + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles != NULL) { return (oc_sec_cred_t *)oc_list_head(roles->roles); } return NULL; } 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 = get_roles_for_client(client); - if (roles) { - oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); - while (r) { - if (role == r) { - oc_list_remove(roles->roles, r); - mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&x509_crt_s, r->ctx); - free_cred_properties(r); - oc_memb_free(&roles_s, r); - return; - } - r = r->next; + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles == NULL) { + 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); + sec_free_role(r); + return true; } + r = r->next; } + return false; } -void +int oc_sec_free_roles_for_device(size_t device) { - oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(clients), *next; - while (roles) { - next = roles->next; + int removed = 0; + oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(g_clients); + 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 = get_roles_for_client(client); - if (roles) { - 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(&x509_crt_s, r->ctx); - free_cred_properties(r); - oc_memb_free(&roles_s, r); - r = (oc_sec_cred_t *)oc_list_pop(roles->roles); - } - oc_list_remove(clients, roles); - oc_memb_free(&clients_s, roles); + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles == NULL) { + return 0; + } + + int removed = 0; + oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_pop(roles->roles); + 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 = get_roles_for_client(client); - if (roles) { - oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); - while (r) { - if (r->credid == credid) { - oc_list_remove(roles->roles, r); - mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&x509_crt_s, r->ctx); - free_cred_properties(r); - oc_memb_free(&roles_s, r); - return 0; + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles == NULL) { + return false; + } + oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); + while (r != NULL) { + if (r->credid == credid) { + oc_list_remove(roles->roles, r); + sec_free_role(r); + return true; + } + r = r->next; + } + return false; +} + +static void +oc_sec_encode_roles(const oc_tls_peer_t *client, size_t device, + oc_interface_mask_t iface_mask) +{ + oc_sec_cred_t *cr = oc_sec_roles_get(client); + oc_rep_start_root_object(); + if ((iface_mask & OC_IF_BASELINE) != 0) { + oc_process_baseline_interface( + oc_core_get_resource_by_index(OCF_SEC_ROLES, device)); + } + oc_rep_set_array(root, roles); + while (cr != NULL) { + oc_rep_object_array_start_item(roles); + /* credid */ + oc_rep_set_int(roles, credid, cr->credid); + /* credtype */ + oc_rep_set_int(roles, credtype, cr->credtype); + /* credusage */ + oc_string_view_t credusage_string = + oc_cred_credusage_to_string(cr->credusage); + if (credusage_string.length > 4) { + oc_rep_set_text_string_v1(roles, credusage, credusage_string.data, + credusage_string.length); + } + /* publicdata */ + if (oc_string_len(cr->publicdata.data) > 0) { + oc_rep_set_object(roles, publicdata); + if (cr->publicdata.encoding == OC_ENCODING_PEM) { + oc_rep_set_text_string_v1(publicdata, data, + oc_string(cr->publicdata.data), + oc_string_len(cr->publicdata.data)); + } else { + oc_rep_set_byte_string(publicdata, data, + oc_cast(cr->publicdata.data, const uint8_t), + oc_string_len(cr->publicdata.data)); } - r = r->next; + oc_string_view_t encoding_string = + oc_cred_encoding_to_string(cr->publicdata.encoding); + if (encoding_string.length > 7) { + oc_rep_set_text_string_v1(publicdata, encoding, encoding_string.data, + encoding_string.length); + } + oc_rep_close_object(roles, publicdata); } + oc_rep_object_array_end_item(roles); + cr = cr->next; } - return -1; + oc_rep_close_array(root, roles); + oc_rep_end_root_object(); +} + +static void +roles_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)data; + const oc_tls_peer_t *client = oc_tls_get_peer(request->origin); + oc_sec_encode_roles(client, request->resource->device, iface_mask); + oc_send_response_with_callback(request, OC_STATUS_OK, true); +} + +static void +roles_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; + if (oc_sec_apply_cred(request->request_payload, request->resource, + request->origin, + /*on_apply_cred_cb*/ NULL, + /*on_apply_cred_data*/ NULL) != 0) { + oc_send_response_with_callback(request, OC_STATUS_BAD_REQUEST, true); + return; + } + oc_send_response_with_callback(request, OC_STATUS_CHANGED, true); +} + +static void +roles_resource_delete(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; + 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) { + // 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); +} + +void +oc_sec_roles_create_resource(size_t device) +{ + oc_core_populate_resource(OCF_SEC_ROLES, device, OCF_SEC_ROLES_URI, + (oc_interface_mask_t)OCF_SEC_ROLES_IF_MASK, + (oc_interface_mask_t)OCF_SEC_ROLES_DEFAULT_IF, + OC_DISCOVERABLE | OC_SECURE, roles_resource_get, + /*put*/ NULL, roles_resource_post, + roles_resource_delete, 1, OCF_SEC_ROLES_RT); } -#else /* OC_PKI */ -typedef int dummy_declaration; -#endif /* !OC_PKI */ -#endif /* OC_SECURITY */ +#endif /* OC_SECURITY && OC_PKI */ diff --git a/security/oc_roles_internal.h b/security/oc_roles_internal.h index 5d3c4d04ea..7a48a21664 100644 --- a/security/oc_roles_internal.h +++ b/security/oc_roles_internal.h @@ -19,28 +19,138 @@ #ifndef OC_ROLES_INTERNAL_H #define OC_ROLES_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_role.h" #include "security/oc_cred_internal.h" #include "security/oc_tls_internal.h" +#include "util/oc_compiler.h" + +#include +#include #ifdef __cplusplus extern "C" { #endif -/* Used on the server-side for handling role assertions via /oic/sec/roles */ -oc_sec_cred_t *oc_sec_allocate_role(const oc_tls_peer_t *client, size_t device); -void oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client); -oc_sec_cred_t *oc_sec_get_roles(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); +#define OCF_SEC_ROLES_URI "/oic/sec/roles" +#define OCF_SEC_ROLES_RT "oic.r.roles" + +enum { + OCF_SEC_ROLES_IF_MASK = OC_IF_BASELINE | OC_IF_RW, + OCF_SEC_ROLES_DEFAULT_IF = OC_IF_RW, + + OCF_SEC_ROLES_MAX_NUM = 2, +}; + +/** + * \defgroup server-roles Event timers + * + * Server-side API for handling role assertions via /oic/sec/roles + * + * @{ + */ + +/** Get head of the list of roles asserted by the client. */ +oc_sec_cred_t *oc_sec_roles_get(const oc_tls_peer_t *client) OC_NONNULL(); + +/** + * @brief Add a role to the list of roles asserted by the client. + * + * @param client client asserting the role (cannot be NULL) + * @param device device index + * + * @return newly allocated role on success + * @return NULL on failure + * + */ +oc_sec_cred_t *oc_sec_roles_add(const oc_tls_peer_t *client, size_t device) + OC_NONNULL(); + +/** + * @brief Create roles (/oic/sec/roles) resource for given device. + * + * @param device device index + */ +void oc_sec_roles_create_resource(size_t device); + +/** + * @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 -/* Used on the client-side for asserting roles that had been provisioned to +enum { + OC_ROLES_NUM_ROLE_CREDS = 2, +}; + +/** + * \defgroup client-roles Event timers + * + * Client-side API for asserting roles that had been provisioned to * /oic/sec/cred. + * + * @{ */ -void oc_sec_remove_role_cred(const char *role, const char *authority); -oc_role_t *oc_sec_add_role_cred(const char *role, const char *authority); -oc_role_t *oc_sec_get_role_creds(void); + +/** @brief Add a role (if it doesn't exist) to the list of roles asserted by the + * client. */ +oc_role_t *oc_sec_role_cred_add_or_get(oc_string_view_t role, + oc_string_view_t authority); + +/** @brief Remove a role from the list of roles asserted by the client. */ +bool oc_sec_role_cred_remove(oc_string_view_t role, oc_string_view_t authority); + +/** @brief Get the list of roles asserted by the client. */ +oc_role_t *oc_sec_role_creds_get(void); + +/** @brief Free the list of roles asserted by the client. */ +void oc_sec_role_creds_free(void); + +// TODO: if a cred is removed, the list of roles asserted by the client should +// be refreshed and if no longer asserted, the role should be removed from the +// list of roles asserted by the client. + +/** @} */ + +#endif /* OC_CLIENT */ #ifdef __cplusplus } diff --git a/security/oc_svr.c b/security/oc_svr.c index 60789adf0c..bf2550f03c 100644 --- a/security/oc_svr.c +++ b/security/oc_svr.c @@ -18,7 +18,6 @@ #ifdef OC_SECURITY -#include "oc_svr_internal.h" #include "api/oc_core_res_internal.h" #include "oc_acl_internal.h" #include "oc_ael.h" @@ -30,8 +29,10 @@ #include "oc_doxm_internal.h" #include "oc_pstat.h" #include "oc_ri.h" +#include "oc_roles_internal.h" #include "oc_sdi_internal.h" #include "oc_sp_internal.h" +#include "oc_svr_internal.h" #include "port/oc_log_internal.h" void @@ -57,10 +58,7 @@ oc_sec_svr_create(void) OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, OC_DISCOVERABLE | OC_SECURE, get_acl, 0, post_acl, delete_acl, 1, "oic.r.acl2"); - oc_core_populate_resource(OCF_SEC_CRED, i, "/oic/sec/cred", - OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, - OC_DISCOVERABLE | OC_SECURE, get_cred, 0, - post_cred, delete_cred, 1, "oic.r.cred"); + oc_sec_cred_create_resource(i); oc_core_populate_resource( OCF_SEC_AEL, i, "/oic/sec/ael", OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, OC_DISCOVERABLE | OC_SECURE, get_ael, 0, post_ael, 0, 1, "oic.r.ael"); @@ -69,11 +67,7 @@ oc_sec_svr_create(void) oc_sec_sdi_create_resource(i); #ifdef OC_PKI oc_sec_csr_create_resource(i); - - oc_core_populate_resource(OCF_SEC_ROLES, i, "/oic/sec/roles", - OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, - OC_DISCOVERABLE | OC_SECURE, get_cred, 0, - post_cred, delete_cred, 1, "oic.r.roles"); + oc_sec_roles_create_resource(i); #endif /* OC_PKI */ } } 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 new file mode 100644 index 0000000000..684fb0bfdd --- /dev/null +++ b/security/unittest/rolestest.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** + * + * Copyright (c) 2023 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#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 }; + +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)); +} + +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 + +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); + + 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 */ + +TEST_F(TestRolesWithServer, PutRequest_FailMethodNotSupported) +{ + const oc_endpoint_t *ep = oc::TestDevice::GetEndpoint(kDeviceID, 0, SECURED); + ASSERT_NE(nullptr, ep); + +#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM + oc_status_t error_code = OC_STATUS_METHOD_NOT_ALLOWED; +#else /* !OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + oc_status_t error_code = OC_STATUS_UNAUTHORIZED; +#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + oc::testNotSupportedMethod(OC_PUT, ep, OCF_SEC_ROLES_URI, nullptr, + error_code); +} + +#ifdef OC_CLIENT + +class TestRoleCreds : public testing::Test { +public: + void TearDown() override { oc_sec_role_creds_free(); } + + static size_t countRoleCreds() + { + size_t count = 0; + const oc_role_t *role = oc_sec_role_creds_get(); + while (role != nullptr) { + ++count; + role = role->next; + } + return count; + } +}; + +TEST_F(TestRoleCreds, Add) +{ + ASSERT_EQ(0, countRoleCreds()); + + std::string role = "role"; + std::string authority = "authority"; + oc_role_t *role_cred = oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + EXPECT_EQ(1, countRoleCreds()); + + // adding the same role-authority pair should return the same role_cred + oc_role_t *role_cred2 = oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred2); + EXPECT_EQ(1, countRoleCreds()); + EXPECT_EQ(role_cred, role_cred2); + +#ifdef OC_DYNAMIC_ALLOCATION + // different role + std::string role2 = "role2"; + oc_role_t *role_cred3 = + oc_sec_role_cred_add_or_get({ role2.c_str(), role2.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred3); + EXPECT_EQ(2, countRoleCreds()); + EXPECT_NE(role_cred, role_cred3); + + // different authority + std::string authority2 = "authority2"; + oc_role_t *role_cred4 = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority2.c_str(), authority2.length() }); + EXPECT_NE(nullptr, role_cred4); + EXPECT_EQ(3, countRoleCreds()); + EXPECT_NE(role_cred, role_cred4); +#endif /* OC_DYNAMIC_ALLOCATION */ +} + +TEST_F(TestRoleCreds, Add_Fail) +{ + ASSERT_EQ(0, countRoleCreds()); + + // missing role + std::string authority = "authority"; + EXPECT_EQ(nullptr, + oc_sec_role_cred_add_or_get( + { nullptr, 0 }, { authority.c_str(), authority.length() })); + EXPECT_EQ(0, countRoleCreds()); + + // missing authority + std::string role = "role"; + EXPECT_EQ(nullptr, oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, { nullptr, 0 })); + EXPECT_EQ(0, countRoleCreds()); + +#ifndef OC_DYNAMIC_ALLOCATION + // without dynamic allocation, we can allocate only OC_ROLES_NUM_ROLE_CREDS + // role creds + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + oc_role_t *role_cred = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + EXPECT_EQ(i + 1, countRoleCreds()); + } + // adding one more should fail + EXPECT_EQ(nullptr, oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, + { authority.c_str(), authority.length() })); + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS, countRoleCreds()); +#endif /* !OC_DYNAMIC_ALLOCATION */ +} + +TEST_F(TestRoleCreds, Remove) +{ + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + oc_role_t *role_cred = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + } + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS, countRoleCreds()); + + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + EXPECT_TRUE( + oc_sec_role_cred_remove({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() })); + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS - i - 1, countRoleCreds()); + } + EXPECT_EQ(0, countRoleCreds()); +} + +TEST_F(TestRoleCreds, RemoveFail) +{ + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + oc_role_t *role_cred = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + } + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS, countRoleCreds()); + + // missing role + std::string authority = "authority0"; + EXPECT_FALSE(oc_sec_role_cred_remove( + { nullptr, 0 }, { authority.c_str(), authority.length() })); + + // missing authority + std::string role = "role0"; + EXPECT_FALSE( + oc_sec_role_cred_remove({ role.c_str(), role.length() }, { nullptr, 0 })); + + // non-existing role-authority pair + std::string role2 = "role2"; + std::string authority2 = "authority2"; + EXPECT_FALSE( + oc_sec_role_cred_remove({ role.c_str(), role.length() }, + { authority2.c_str(), authority2.length() })); + EXPECT_FALSE( + oc_sec_role_cred_remove({ role2.c_str(), role2.length() }, + { authority.c_str(), authority.length() })); + EXPECT_FALSE( + oc_sec_role_cred_remove({ role2.c_str(), role2.length() }, + { authority2.c_str(), authority2.length() })); +} + +// TODO: test oc_assert_role +// TODO: test oc_assert_all_roles + +#endif /* OC_CLIENT */ + +#endif /* OC_SECURITY && OC_PKI */ diff --git a/tests/gtest/tls/DTLS.cpp b/tests/gtest/tls/DTLS.cpp index 470301373e..443f7506fa 100644 --- a/tests/gtest/tls/DTLS.cpp +++ b/tests/gtest/tls/DTLS.cpp @@ -36,10 +36,11 @@ AddPresharedKey(size_t device, const PreSharedKey &psk) std::copy(std::begin(uuid->id), std::end(uuid->id), std::begin(hint)); std::array uuid_str{}; oc_uuid_to_str(uuid, uuid_str.data(), uuid_str.size()); - if (oc_sec_add_new_cred(device, false, nullptr, -1, OC_CREDTYPE_PSK, - OC_CREDUSAGE_NULL, uuid_str.data(), OC_ENCODING_RAW, - psk.size(), psk.data(), OC_ENCODING_UNSUPPORTED, 0, - nullptr, nullptr, nullptr, nullptr, nullptr) == -1) { + if (oc_sec_add_new_cred( + device, false, nullptr, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, + uuid_str.data(), { psk.data(), psk.size(), OC_ENCODING_RAW }, + { nullptr, 0, OC_ENCODING_UNSUPPORTED }, { nullptr, 0 }, { nullptr, 0 }, + { nullptr, 0 }, nullptr) == -1) { return {}; } return hint;