E2EE signing implementation 27/289427/10
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 7 Mar 2023 10:42:32 +0000 (11:42 +0100)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 22 Mar 2023 15:48:51 +0000 (16:48 +0100)
Tests updated.

Change-Id: I61790de9a3914c3bea846e9060360c16969e65c6

13 files changed:
packaging/device-certificate-manager.spec
src/dcm-client/CMakeLists.txt
src/dcm-client/dcm_client.cpp
src/dcm-client/dcm_client.h
src/dcm-client/dcm_support.proto
src/dcm-client/device_certificate_manager.cpp
src/dcm-daemon/CMakeLists.txt
src/dcm-daemon/dcm_session.cpp
src/dcm-daemon/dcm_session.h
src/shared/bundle.cpp [new file with mode: 0644]
src/shared/bundle.h [new file with mode: 0644]
tests/CMakeLists.txt
tests/api_test.cpp

index e5a9d9c..c261bb2 100644 (file)
@@ -16,6 +16,7 @@ BuildRequires: pkgconfig(protobuf-lite)
 BuildRequires: pkgconfig(cynara-client)
 BuildRequires: pkgconfig(cynara-creds-socket)
 BuildRequires: pkgconfig(cynara-session)
+BuildRequires: pkgconfig(openssl1.1)
 BuildRequires: boost-devel
 %if "%{build_type}" == "COVERAGE"
 BuildRequires: lcov
index e29b59e..fc8b553 100644 (file)
@@ -40,6 +40,7 @@ ADD_LIBRARY(${TARGET_CLIENT}
        device_certificate_manager.cpp
        ../shared/protobuf_asio.cpp
        ../shared/log.cpp
+       ../shared/bundle.cpp
        ${PROTO_SRCS}
        ${PROTO_HDRS})
 
index 9bac6b4..6797fc4 100644 (file)
@@ -268,6 +268,67 @@ int dcm_client_connection::sign_data(
        return DCM_ERROR_NONE;
 }
 
+int dcm_client_connection::e2ee_sign_data(
+       dcm_digest_algorithm_e md,
+       const void* payload, size_t payload_size,
+       std::vector<uint8_t>& signed_message,
+       std::vector<uint8_t>& digest) noexcept
+{
+       std::lock_guard<std::mutex> locker(fLock);
+
+       if(!fCookie) {
+               LOGE("Trying to request E2EE data signing in object %p but there is no connection", this);
+               return DCM_ERROR_SOCKET;
+       }
+
+       try {
+               RequestMessage request;
+               ResponseMessage response;
+
+               auto* sign_req = request.mutable_e2ee_sign_data();
+               sign_req->set_context_cookie(fCookie);
+               sign_req->set_data_to_sign(payload, payload_size);
+               sign_req->set_digest_type(static_cast<MessageDigestType>(md));
+
+               send_receive(request, response);
+               if(!response.has_sign_data()) {
+                       LOGE("Response for E2EE signature has no signature data");
+                       return DCM_ERROR_NO_DATA;
+               }
+
+               auto& sign_resp(response.sign_data());
+               if(sign_resp.result() != 0) {
+                       LOGE("E2EE signature request for session %p received error %d", this, sign_resp.result());
+                       return DCM_ERROR_NO_DATA;
+               }
+
+               const auto& signature = sign_resp.signature();
+               if(signature.empty()) {
+                       LOGE("Received E2EE signature object is empty for session %p", this);
+                       return DCM_ERROR_NO_DATA;
+               }
+
+               digest.resize(signature.size());
+               memcpy(&digest[0], signature.c_str(), signature.size());
+
+               const auto& message = sign_resp.message();
+               if(message.empty()) {
+                       LOGE("Received E2EE message object is empty for session %p", this);
+                       return DCM_ERROR_NO_DATA;
+               }
+               signed_message.resize(message.size());
+               memcpy(&signed_message[0], message.c_str(), message.size());
+       } catch(std::bad_alloc&) {
+               LOGE("Out of memory when processing E2EE sign request for session %p", this);
+               return DCM_ERROR_OUT_OF_MEMORY;
+       } catch(...) {
+               LOGE("When processing E2EE signature for session %p got exception : %s", this, "Unknown error");
+               return DCM_ERROR_UNKNOWN;
+       }
+
+       return DCM_ERROR_NONE;
+}
+
 void dcm_client_connection::send_receive(RequestMessage& request, ResponseMessage& response)
 {
        protobuf_sync_message_serialization(*fSocket).encodeMessage(request);
index 1058485..3e9488f 100644 (file)
@@ -56,6 +56,12 @@ public:
                const void* hash_data, size_t hash_size,
                std::vector<uint8_t>& digest) noexcept;
 
+       int e2ee_sign_data(
+               dcm_digest_algorithm_e md,
+               const void* hash_data, size_t hash_size,
+               std::vector<uint8_t>& message,
+               std::vector<uint8_t>& digest) noexcept;
+
 private:
        void send_receive(RequestMessage& request, ResponseMessage& response);
 
index 50a2f43..1033296 100644 (file)
@@ -57,6 +57,7 @@ message SignResponse
 {
        required int32 result = 1;
        optional bytes signature = 2;
+       optional bytes message = 3;
 }
 
 message ExtCallRequest
@@ -77,6 +78,7 @@ message RequestMessage {
                RequestCertificateChain request_chain = 2;
                SignRequest sign_data = 3;
                ExtCallRequest ext_call = 4;
+               SignRequest e2ee_sign_data = 5;
        }
 }
 
@@ -86,5 +88,6 @@ message ResponseMessage {
                RequestCertificateChainResponse request_chain = 2;
                SignResponse sign_data = 3;
                ExtCallResponse ext_call = 4;
+
        }
 }
index 62ba34f..55e374e 100644 (file)
@@ -24,6 +24,7 @@
 #include "device_certificate_manager.h"
 #include "dcm_client.h"
 #include "log.h"
+#include "bundle.h"
 
 #ifndef API_DEVICE_CERTIFICATE_MANAGER_EXPORT
 #define API_DEVICE_CERTIFICATE_MANAGER_EXPORT __attribute__((visibility("default")))
@@ -174,34 +175,19 @@ int dcm_e2ee_create_bundle(unsigned char *message,
        if (message == NULL || bundle == NULL)
                return DCM_ERROR_INVALID_PARAMETER;
 
-       const char* message_str = reinterpret_cast<const char*>(message);
+       struct dcm_e2ee_bundle_s *tmp = static_cast<struct dcm_e2ee_bundle_s*>(
+               malloc(sizeof(struct dcm_e2ee_bundle_s)));
 
-       struct dcm_e2ee_bundle_s *tmp = new dcm_e2ee_bundle_s;
-
-       tmp->message_len = message_len;
-       tmp->pkg_id_offset = strnlen(message_str, message_len);
-       if (tmp->pkg_id_offset == message_len) {
-               delete tmp;
-               return DCM_ERROR_INVALID_PARAMETER; // wrong format
-       }
-
-       if (strcmp(message_str, "Tizen") != 0) { // TODO specify platform format
-               delete tmp;
-               return DCM_ERROR_NOT_SUPPORTED; // not a Tizen bundle
-       }
-
-       tmp->pkg_id_offset++; // skip \0
-       message_str += tmp->pkg_id_offset;
-       message_len -= tmp->pkg_id_offset;
+       if (tmp == NULL)
+               return DCM_ERROR_OUT_OF_MEMORY;
 
-       tmp->payload_offset = strnlen(message_str, message_len);
-       if (tmp->payload_offset == message_len) {
-               delete tmp;
-               return DCM_ERROR_INVALID_PARAMETER; // wrong format
+       int ret = decompose_e2ee_message(message, message_len, tmp->pkg_id_offset, tmp->payload_offset);
+       if (ret != DCM_ERROR_NONE) {
+               free(tmp);
+               return ret;
        }
-       tmp->payload_offset += tmp->pkg_id_offset; // skip pkg_id
-       tmp->payload_offset++; // skip \0
 
+       tmp->message_len = message_len;
        tmp->message = message;
 
        *bundle = tmp;
@@ -215,7 +201,7 @@ void dcm_e2ee_free_bundle(dcm_e2ee_bundle_h bundle)
                return;
 
        free(bundle->message);
-       delete bundle;
+       free(bundle);
 }
 
 API_DEVICE_CERTIFICATE_MANAGER_EXPORT
@@ -274,9 +260,9 @@ int dcm_e2ee_get_bundle_payload(const dcm_e2ee_bundle_h bundle,
 
 API_DEVICE_CERTIFICATE_MANAGER_EXPORT
 int dcm_e2ee_create_signed_bundle(const void *key_ctx,
-       dcm_digest_algorithm_e /*md*/,
+       dcm_digest_algorithm_e md,
        const unsigned char *payload,
-       size_t /*payload_len*/,
+       size_t payload_len,
        dcm_e2ee_bundle_h *bundle,
        char **signature,
        size_t *signature_len)
@@ -285,12 +271,29 @@ int dcm_e2ee_create_signed_bundle(const void *key_ctx,
                signature_len == NULL)
                return DCM_ERROR_INVALID_PARAMETER;
 
-       /* TODO:
-        * 1. Get client pkg_id from socket
-        * 2. Construct a message (platform="Tizen" + pkg_id + payload) and put it into bundle
-        * 3. Sign the message with OCF
-        * 4. Return bundle and signature
-        */
+       const dcm_key_context_internal *context =
+               reinterpret_cast<const dcm_key_context_internal *>(key_ctx);
 
-       return DCM_ERROR_NONE;
+       std::vector<uint8_t> digest;
+       std::vector<uint8_t> message;
+       int result = context->connection->e2ee_sign_data(md, payload, payload_len, message, digest);
+       if(result == DCM_ERROR_NONE) {
+               *signature = static_cast<char*>(malloc(sizeof(uint8_t) * digest.size()));
+               if(*signature == NULL)
+                       return DCM_ERROR_OUT_OF_MEMORY;
+               memcpy(*signature, digest.data(), digest.size());
+               *signature_len = digest.size();
+
+               auto message_dup = static_cast<unsigned char*>(malloc(sizeof(uint8_t)*message.size()));
+               if(message_dup == NULL) {
+                       free(signature);
+                       return DCM_ERROR_OUT_OF_MEMORY;
+               }
+               memcpy(message_dup, message.data(), message.size());
+
+               result = dcm_e2ee_create_bundle(message_dup, message.size(), bundle);
+               assert(result == DCM_ERROR_NONE);
+       }
+
+       return result;
 }
index 856d699..b661294 100644 (file)
@@ -26,6 +26,7 @@ FIND_PACKAGE(Boost REQUIRED
 PKG_CHECK_MODULES(DAEMON_DEPS
        REQUIRED
        libsystemd
+       openssl1.1
        cynara-client
        cynara-creds-socket
        cynara-session
@@ -44,6 +45,7 @@ ADD_EXECUTABLE(${TARGET_DAEMON}
        dcm_session.cpp
        ../shared/protobuf_asio.cpp
        ../shared/log.cpp
+       ../shared/bundle.cpp
        soresolver.cpp
        ${PROTO_SRCS}
        ${PROTO_HDRS})
index cb429a7..7e9b2c9 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <iostream>
 #include <cassert>
+#include <openssl/evp.h>
 
 #include <cynara-client.h>
 #include <cynara-creds-socket.h>
 #include "dcm_session.h"
 #include "dcm_server.h"
 #include "../dcm-client/device_certificate_manager_ext_types.h"
+#include "../dcm-client/device_certificate_manager.h"
 #include "log.h"
+#include "bundle.h"
+
+namespace {
+const EVP_MD* get_md(MessageDigestType digest_type)
+{
+       switch(digest_type) {
+               case MD_MD2:
+                       return EVP_md2();
+               case MD_MD4:
+                       return EVP_md4();
+               case MD_MD5:
+                       return EVP_md5();
+               case MD_SHA1:
+                       return EVP_sha1();
+               case MD_SHA224:
+                       return EVP_sha224();
+               case MD_SHA256:
+                       return EVP_sha256();
+               case MD_SHA384:
+                       return EVP_sha384();
+               case MD_SHA512:
+                       return EVP_sha512();
+               case MD_RIPEMD160:
+                       return EVP_ripemd160();
+               default:
+                       LOGE("Unknown message digest type: " << static_cast<unsigned>(digest_type));
+                       return nullptr;
+               }
+}
+
+std::string digest_message(const std::string& message, MessageDigestType digest_type)
+{
+       if (digest_type == MD_NONE)
+               return message;
+
+       EVP_MD_CTX* mdctx = nullptr;
+       if((mdctx = EVP_MD_CTX_new()) == nullptr)
+               return std::string();
+
+       std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> mdctxPtr(mdctx, EVP_MD_CTX_free);
+
+       auto md = get_md(digest_type);
+       if (md == nullptr)
+               return std::string();
+
+       if(1 != EVP_DigestInit_ex(mdctx, md, nullptr))
+               return std::string();
+
+       if(1 != EVP_DigestUpdate(
+               mdctx,
+               reinterpret_cast<const unsigned char*>(message.c_str()),
+               message.size()))
+               return std::string();
+
+       std::string digest(EVP_MD_size(md), '\0');
+       size_t digest_size;
+       if(1 != EVP_DigestFinal_ex(
+               mdctx,
+               reinterpret_cast<unsigned char*>(digest.data()),
+               &digest_size))
+               return std::string();
+
+       digest.resize(digest_size);
+       return digest;
+}
+} // namespace
 
 #define DCM_DEFAULT_PRIVILEGE "http://tizen.org/privilege/devicecertificate"
 
@@ -108,6 +176,9 @@ void dcm_session::decode_message() noexcept
                        case RequestMessage::kExtCall:
                                handle_ext_call_request(requestMessage.ext_call());
                                break;
+                       case RequestMessage::kE2EeSignData:
+                               handle_e2ee_sign_request(requestMessage.e2ee_sign_data());
+                               break;
                        default:
                                LOGE("Incorrect request message type");
                                // This will terminate connection
@@ -327,6 +398,35 @@ void dcm_session::handle_cert_chain(const RequestCertificateChain& message)
        reply(msg);
 }
 
+int dcm_session::sign_request_check(const SignRequest& message)
+{
+       if(message.context_cookie() != fCookie) {
+               LOGE("Received unknown context cookie");
+               return false;
+       }
+
+       if(!fBackendContext) {
+               LOGE("Context not associated with connection");
+               return false;
+       }
+
+       if(message.data_to_sign().size() == 0) {
+               LOGE("Data to sign is empty");
+               return false;
+       }
+
+       return true;
+}
+
+int dcm_session::sign(
+       MessageDigestType digest_type,
+       const std::string& message,
+       std::string& signature)
+{
+       return fSoResolver->invoke<int, dcm_backend_context&, MessageDigestType, const std::string&, std::string&>(
+               "dcm_backend_sign_crypto_data", *fBackendContext, digest_type, message, signature);
+}
+
 void dcm_session::handle_sign_request(const SignRequest& message)
 {
        LOGD("Request data signing");
@@ -336,33 +436,53 @@ void dcm_session::handle_sign_request(const SignRequest& message)
                return;
        }
 
+       int ret;
        ResponseMessage msg;
        auto* signingResponse = msg.mutable_sign_data();
-
-       if(message.context_cookie() != fCookie) {
-               LOGE("Received unknown context cookie");
-               signingResponse->set_result(-EINVAL);
-               reply(msg);
-               return;
+       if (!sign_request_check(message)) {
+               ret = -EINVAL;
+       } else {
+               ret = sign(
+                       message.digest_type(), message.data_to_sign(), *signingResponse->mutable_signature());
        }
+       signingResponse->set_result(ret);
+       reply(msg);
+}
 
-       if(!fBackendContext) {
-               LOGE("Context not associated with connection");
-               signingResponse->set_result(-EINVAL);
-               reply(msg);
-               return;
-       }
+void dcm_session::handle_e2ee_sign_request(const SignRequest& message)
+{
+       LOGD("Request E2EE data signing");
 
-       if(message.data_to_sign().size() == 0) {
-               LOGE("Data to sign is empty");
-               signingResponse->set_result(-EINVAL);
+       if(!verify_privileges(fSocket.native_handle(), DCM_DEFAULT_PRIVILEGE)) {
+               LOGE("Client privilege check failure. Disconnect");
                return;
        }
 
-       int error = fSoResolver->invoke<int, dcm_backend_context&, MessageDigestType, const std::string&, std::string&>(
-                       "dcm_backend_sign_crypto_data", *fBackendContext, message.digest_type(), message.data_to_sign(), *signingResponse->mutable_signature());
+       int ret;
+       ResponseMessage msg;
+       auto* signingResponse = msg.mutable_sign_data();
 
-       signingResponse->set_result(error);
+       if (!sign_request_check(message)) {
+               ret = -EINVAL;
+       } else {
+               std::unique_ptr<char, string_free_deleter> client;
+               char* tmp_str = nullptr;
+               ret = cynara_creds_socket_get_client(
+                       fSocket.native_handle(), CLIENT_METHOD_DEFAULT, &tmp_str);
+               assert(ret == CYNARA_API_SUCCESS); // it was checked in verify_privileges already
+               client.reset(tmp_str);
+
+               std::string composed = compose_e2ee_message(client.get(), message.data_to_sign());
+               auto digest = digest_message(composed, message.digest_type());
+               if (digest.empty()) {
+                       LOGE("Digest calculation failed");
+                       ret = -EINVAL; // TODO validate digest algorithms and return proper error to client
+               } else {
+                       ret = sign(message.digest_type(), digest, *signingResponse->mutable_signature());
+                       signingResponse->set_message(composed);
+               }
+       }
+       signingResponse->set_result(ret);
        reply(msg);
 }
 
index dbb095d..362d4b3 100644 (file)
@@ -60,8 +60,12 @@ private:
        void handle_context_association(const AssociateKeyContext& message);
        void handle_cert_chain(const RequestCertificateChain& message);
        void handle_sign_request(const SignRequest& message);
+       void handle_e2ee_sign_request(const SignRequest& message);
        void handle_ext_call_request(const ExtCallRequest& message);
 
+       int sign(MessageDigestType digest_type, const std::string& message, std::string& signature);
+       int sign_request_check(const SignRequest& message);
+
 private:
        boost::asio::local::stream_protocol::socket             fSocket;
        protobuf_async_message_serialization                    fSerializer;
diff --git a/src/shared/bundle.cpp b/src/shared/bundle.cpp
new file mode 100644 (file)
index 0000000..2921cf7
--- /dev/null
@@ -0,0 +1,80 @@
+/******************************************************************
+ *
+ * Copyright 2023 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include <cerrno>
+#include <cassert>
+#include <cstring>
+
+#include "bundle.h"
+#include "../dcm-client/device_certificate_manager.h"
+
+namespace {
+constexpr char PLATFORM[] = "Tizen";
+}
+
+std::string compose_e2ee_message(const char* pkg_id, const std::string& payload)
+{
+       assert(pkg_id);
+
+       size_t platform_len = strlen(PLATFORM) + 1;
+       size_t pkg_id_len = strlen(pkg_id) + 1;
+       size_t size = platform_len + pkg_id_len + payload.size();
+
+       std::string message(size, ' ');
+
+       auto tmp = message.data();
+
+       memcpy(tmp, PLATFORM, platform_len);
+       tmp += platform_len;
+       memcpy(tmp, pkg_id, pkg_id_len);
+       tmp += pkg_id_len;
+       memcpy(tmp, payload.c_str(), payload.size());
+
+       return message;
+}
+
+int decompose_e2ee_message(
+       const unsigned char* message,
+       size_t message_len,
+       size_t& pkg_id_offset,
+       size_t& payload_offset)
+{
+       if (message == NULL)
+               return DCM_ERROR_INVALID_PARAMETER;
+
+       const char* message_str = reinterpret_cast<const char*>(message);
+       pkg_id_offset = strnlen(message_str, message_len);
+       if (pkg_id_offset == message_len)
+               return DCM_ERROR_INVALID_PARAMETER; // wrong format
+
+       if (strcmp(message_str, PLATFORM) != 0)
+               return DCM_ERROR_NOT_SUPPORTED; // not a Tizen bundle
+
+       pkg_id_offset++; // skip \0
+       message_str += pkg_id_offset;
+       message_len -= pkg_id_offset;
+
+       payload_offset = strnlen(message_str, message_len);
+       if (payload_offset == message_len)
+               return DCM_ERROR_INVALID_PARAMETER; // wrong format
+
+       payload_offset += pkg_id_offset; // skip pkg_id
+       payload_offset++; // skip \0
+
+       return DCM_ERROR_NONE;
+}
diff --git a/src/shared/bundle.h b/src/shared/bundle.h
new file mode 100644 (file)
index 0000000..9e3e5ff
--- /dev/null
@@ -0,0 +1,28 @@
+/******************************************************************
+ *
+ * Copyright 2023 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+std::string compose_e2ee_message(const char* pkg_id, const std::string& payload);
+int decompose_e2ee_message(
+       const unsigned char* message,
+       size_t message_len,
+       size_t& pkg_id_offset,
+       size_t& payload_offset);
index 75d3650..3f0aed4 100644 (file)
@@ -48,7 +48,7 @@ FIND_PACKAGE(Boost REQUIRED
        COMPONENTS
        unit_test_framework)
 
-PKG_CHECK_MODULES(TEST_DEPS REQUIRED dlog)
+PKG_CHECK_MODULES(TEST_DEPS REQUIRED dlog libsmack)
 
 INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS} ${TEST_DEPS_INCLUDE_DIRS})
 LINK_DIRECTORIES(${Boost_LIBRARY_DIRS} ${TEST_DEPS_LIBRARY_DIRS})
@@ -68,6 +68,7 @@ ADD_EXECUTABLE(${TARGET_TESTS}
        ../src/dcm-client/device_certificate_manager_ext.cpp
        ../src/shared/log.cpp
        ../src/shared/protobuf_asio.cpp
+       ../src/shared/bundle.cpp
        ${PROTO_SRCS}
        ${PROTO_HDRS})
 
index 5cfb254..0941fb5 100644 (file)
@@ -151,6 +151,7 @@ void freeKey(void* ptr)
 typedef Free<dcm_e2ee_bundle_s, dcm_e2ee_free_bundle> FreeBundle;
 typedef Free<void, freeKey> FreeKey;
 typedef std::unique_ptr<unsigned char, decltype(&free)> BytesPtr;
+typedef std::unique_ptr<char, decltype(&free)> CharPtr;
 
 } // namespace
 
@@ -466,7 +467,7 @@ NEGATIVE_TEST_CASE(test19_dcm_api_no_mem) {
             nullptr, nullptr, nullptr, &ctx) == DCM_ERROR_UNKNOWN);
 }
 
-POSITIVE_TEST_CASE(test20_dcm_api_bundle) {
+POSITIVE_TEST_CASE(test20_dcm_e2ee_bundle_api) {
     BOOST_REQUIRE_NO_THROW(dcm_e2ee_free_bundle(NULL));
 
     unsigned char payload[] = {1,2,3,4};
@@ -495,11 +496,9 @@ POSITIVE_TEST_CASE(test20_dcm_api_bundle) {
     assert_positive(dcm_e2ee_get_bundle_payload, bundle, &cPayload, &cPayloadSize);
     BOOST_REQUIRE_EQUAL(cPayloadSize, sizeof(payload));
     BOOST_REQUIRE_EQUAL(memcmp(payload, cPayload, cPayloadSize), 0);
-
-    // TODO create signed bundle
 }
 
-NEGATIVE_TEST_CASE(test21_dcm_api_bundle) {
+NEGATIVE_TEST_CASE(test21_dcm_e2ee_bundle_api) {
     unsigned char payload[] = {1,2,3,4};
     unsigned char payload2[] = {'T', 'i', 'z', 'e', 'n', 0, 1, 2, 3, 4};
     auto [msg, size] = makeMessage("Tizen", "User::Pkg::test", payload);
@@ -518,81 +517,127 @@ NEGATIVE_TEST_CASE(test21_dcm_api_bundle) {
 
     assert_positive(dcm_e2ee_create_bundle, msg, size, &bundle);
     msgPtr.release();
-    {
-        FreeBundle freeBundle(bundle);
-
-        const unsigned char* cMsg;
-        size_t cMsgSize;
-        assert_invalid_param(dcm_e2ee_get_bundle_message, nullptr, &cMsg,   &cMsgSize);
-        assert_invalid_param(dcm_e2ee_get_bundle_message, bundle,  nullptr, &cMsgSize);
-        assert_invalid_param(dcm_e2ee_get_bundle_message, bundle,  &cMsg,   nullptr);
-
-        const char* cPlatform;
-        assert_invalid_param(dcm_e2ee_get_bundle_platform, nullptr, &cPlatform);
-        assert_invalid_param(dcm_e2ee_get_bundle_platform, bundle,  nullptr);
-
-        const char* cPkgId;
-        assert_invalid_param(dcm_e2ee_get_bundle_pkg_id, nullptr, &cPkgId);
-        assert_invalid_param(dcm_e2ee_get_bundle_pkg_id, bundle,  nullptr);
-
-        const unsigned char* cPayload;
-        size_t cPayloadSize;
-        assert_invalid_param(dcm_e2ee_get_bundle_payload, nullptr, &cPayload, &cPayloadSize);
-        assert_invalid_param(dcm_e2ee_get_bundle_payload, bundle,  nullptr,   &cPayloadSize);
-        assert_invalid_param(dcm_e2ee_get_bundle_payload, bundle,  &cPayload, nullptr);
-    }
-    bundle = nullptr;
+    FreeBundle freeBundle(bundle);
+
+    const unsigned char* cMsg;
+    size_t cMsgSize;
+    assert_invalid_param(dcm_e2ee_get_bundle_message, nullptr, &cMsg,   &cMsgSize);
+    assert_invalid_param(dcm_e2ee_get_bundle_message, bundle,  nullptr, &cMsgSize);
+    assert_invalid_param(dcm_e2ee_get_bundle_message, bundle,  &cMsg,   nullptr);
 
+    const char* cPlatform;
+    assert_invalid_param(dcm_e2ee_get_bundle_platform, nullptr, &cPlatform);
+    assert_invalid_param(dcm_e2ee_get_bundle_platform, bundle,  nullptr);
+
+    const char* cPkgId;
+    assert_invalid_param(dcm_e2ee_get_bundle_pkg_id, nullptr, &cPkgId);
+    assert_invalid_param(dcm_e2ee_get_bundle_pkg_id, bundle,  nullptr);
+
+    const unsigned char* cPayload;
+    size_t cPayloadSize;
+    assert_invalid_param(dcm_e2ee_get_bundle_payload, nullptr, &cPayload, &cPayloadSize);
+    assert_invalid_param(dcm_e2ee_get_bundle_payload, bundle,  nullptr,   &cPayloadSize);
+    assert_invalid_param(dcm_e2ee_get_bundle_payload, bundle,  &cPayload, nullptr);
+}
+
+POSITIVE_TEST_CASE(test22_dcm_e2ee_sign_api) {
+    unsigned char payload[] = {1,2,3,4};
     void* ocfCtx = nullptr;
+
     assert_positive(dcm_create_key_context, nullptr, nullptr, "ECDSA", &ocfCtx);
     FreeKey freeOcfCtx(ocfCtx);
 
-    char* signature;
-    size_t signatureSize;
-    assert_invalid_param(dcm_e2ee_create_signed_bundle,
-                         nullptr,
-                         DCM_DIGEST_SHA256,
-                         payload,
-                         sizeof(payload),
-                         &bundle,
-                         &signature,
-                         &signatureSize);
-
-    assert_invalid_param(dcm_e2ee_create_signed_bundle,
-                         ocfCtx,
-                         DCM_DIGEST_SHA256,
-                         nullptr,
-                         sizeof(payload),
-                         &bundle,
-                         &signature,
-                         &signatureSize);
-
-    assert_invalid_param(dcm_e2ee_create_signed_bundle,
-                         ocfCtx,
-                         DCM_DIGEST_SHA256,
-                         payload,
-                         sizeof(payload),
-                         nullptr,
-                         &signature,
-                         &signatureSize);
-
-    assert_invalid_param(dcm_e2ee_create_signed_bundle,
-                         ocfCtx,
-                         DCM_DIGEST_SHA256,
-                         payload,
-                         sizeof(payload),
-                         &bundle,
-                         nullptr,
-                         &signatureSize);
-
-    assert_invalid_param(dcm_e2ee_create_signed_bundle,
-                         ocfCtx,
-                         DCM_DIGEST_SHA256,
-                         payload,
-                         sizeof(payload),
-                         &bundle,
-                         &signature,
-                         nullptr);
+    dcm_e2ee_bundle_h bundle = nullptr;
+    char* sig = nullptr;
+    size_t sigSize;
+    assert_positive(dcm_e2ee_create_signed_bundle,
+                    ocfCtx,
+                    DCM_DIGEST_SHA256,
+                    payload,
+                    sizeof(payload),
+                    &bundle,
+                    &sig,
+                    &sigSize);
+    FreeBundle freeBundle(bundle);
+    CharPtr signaturePtr(sig, free);
+    BOOST_REQUIRE(sig != nullptr);
+    BOOST_REQUIRE(sigSize > 0);
+    BOOST_REQUIRE(bundle != nullptr);
+
+    const unsigned char* cMsg;
+    size_t cMsgSize;
+    const char* cPlatform;
+    const char* cPkgId;
+    const unsigned char* cPayload;
+    size_t cPayloadSize;
+    assert_positive(dcm_e2ee_get_bundle_message, bundle, &cMsg, &cMsgSize);
+    assert_positive(dcm_e2ee_get_bundle_platform, bundle, &cPlatform);
+    assert_positive(dcm_e2ee_get_bundle_pkg_id, bundle, &cPkgId);
+    assert_positive(dcm_e2ee_get_bundle_payload, bundle, &cPayload, &cPayloadSize);
+
+    BOOST_REQUIRE(cPlatform != nullptr);
+    BOOST_REQUIRE(cPkgId != nullptr);
+
+    size_t cPlatformSize = strlen(cPlatform) + 1;
+    size_t cPkgIdSize = strlen(cPkgId) + 1;
+
+    BOOST_REQUIRE(cMsgSize > 0);
+    BOOST_REQUIRE(cPayloadSize > 0);
+    BOOST_REQUIRE(cPlatformSize + cPkgIdSize + cPayloadSize == cMsgSize);
+
+    char* label = nullptr;
+    BOOST_REQUIRE(smack_new_label_from_self(&label) >= 0);
+    BOOST_REQUIRE(label != nullptr);
+    std::string labelStr(label);
+    free(label);
+
+    BOOST_REQUIRE(strcmp(cPlatform, "Tizen") == 0);
+    BOOST_REQUIRE(labelStr == cPkgId);
+    BOOST_REQUIRE(memcmp(payload, cPayload, cPayloadSize) == 0);
+}
+
+NEGATIVE_TEST_CASE(test23_dcm_e2ee_sign_api) {
+    unsigned char payload[] = {1,2,3,4};
+    void* ocfCtx = nullptr;
+    assert_positive(dcm_create_key_context, nullptr, nullptr, "ECDSA", &ocfCtx);
+    FreeKey freeOcfCtx(ocfCtx);
+    dcm_e2ee_bundle_h bundle;
+
+    char* sig;
+    size_t sigSize;
+
+    auto invalid = [](const void *key_ctx,
+                      dcm_digest_algorithm_e md,
+                      const unsigned char *payload,
+                      size_t payload_len,
+                      dcm_e2ee_bundle_h *bundle,
+                      char **signature,
+                      size_t *signature_len){
+        assert_invalid_param(dcm_e2ee_create_signed_bundle,
+                             key_ctx,
+                             md,
+                             payload,
+                             payload_len,
+                             bundle,
+                             signature,
+                             signature_len);
+    };
+
+    invalid(nullptr, DCM_DIGEST_SHA256, payload, sizeof(payload), &bundle, &sig, &sigSize);
+
+    // unsupported md
+    invalid(ocfCtx, DCM_DIGEST_NONE, payload, sizeof(payload), &bundle, &sig, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_MD2, payload, sizeof(payload), &bundle, &sig, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_MD4, payload, sizeof(payload), &bundle, &sig, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_MD5, payload, sizeof(payload), &bundle, &sig, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_RIPEMD160, payload, sizeof(payload), &bundle, &sig, &sigSize);
+
+    // dcm client will abort for values outside dcm_digest_algorithm_e range
+
+    invalid(ocfCtx, DCM_DIGEST_SHA256, nullptr, sizeof(payload), &bundle, &sig, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_SHA256, payload, sizeof(payload), nullptr, &sig, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_SHA256, payload, sizeof(payload), &bundle, nullptr, &sigSize);
+    invalid(ocfCtx, DCM_DIGEST_SHA256, payload, sizeof(payload), &bundle, &sig, nullptr);
 }
 
 BOOST_AUTO_TEST_SUITE_END()