Add encryption scheme tests 67/48667/21
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 24 Sep 2015 09:21:03 +0000 (11:21 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 27 Oct 2015 06:25:44 +0000 (07:25 +0100)
[Problem] We need tests that will verify correctness of old and new encryption
scheme support.
[Solution] Tests added.

[Verification] Run ckm-tests-internal -t ENCRYPTION_SCHEME_TEST

Change-Id: I9f4e24a9e06684d401540646d5560287e35b828d

tests/CMakeLists.txt
tests/encryption-scheme/CMakeLists.txt
tests/encryption-scheme/scheme-test.cpp
tests/encryption-scheme/scheme-test.h
tests/test_encryption-scheme.cpp [new file with mode: 0644]

index 62fea85..8cadd63 100644 (file)
@@ -29,6 +29,7 @@ INCLUDE_DIRECTORIES(
     ${KEY_MANAGER_PATH}/client-async/
     ${KEY_MANAGER_SRC_PATH}/include
     ${KEY_MANAGER_TEST_MERGED_SRC}/
+    ${KEY_MANAGER_TEST_MERGED_SRC}/encryption-scheme/
     )
 
 SET(TEST_MERGED_SOURCES
@@ -44,6 +45,7 @@ SET(TEST_MERGED_SOURCES
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_comm-manager.cpp
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_serialization.cpp
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_xml-parser.cpp
+    ${KEY_MANAGER_TEST_MERGED_SRC}/test_encryption-scheme.cpp
     ${KEY_MANAGER_PATH}/service/db-crypto.cpp
     ${KEY_MANAGER_PATH}/service/key-provider.cpp
     ${KEY_MANAGER_PATH}/initial-values/parser.cpp
@@ -62,6 +64,7 @@ TARGET_LINK_LIBRARIES(${TARGET_TEST_MERGED}
     ${TARGET_KEY_MANAGER_COMMON}
     ${CMAKE_THREAD_LIBS_INIT}
     ${KEY_MANAGER_DEP_LIBRARIES}
+    ${TARGET_ENCRYPTION_SCHEME_COMMON}
     boost_unit_test_framework
     -ldl
     )
index 8503859..10cea93 100644 (file)
@@ -21,16 +21,39 @@ INCLUDE(FindPkgConfig)
 
 # common encryption scheme library
 PKG_CHECK_MODULES(ENCRYPTION_SCHEME_DEP
-    libsmack
-    REQUIRED)
+    REQUIRED
+    openssl
+    libcrypto
+    libsmack)
+
+FIND_PACKAGE(Threads REQUIRED)
 
 SET(ENCRYPTION_SCHEME_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/smack-access.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/scheme-test.cpp
+
+    ${KEY_MANAGER_PATH}/service/file-lock.cpp
+    ${KEY_MANAGER_PATH}/service/key-provider.cpp
+    ${KEY_MANAGER_PATH}/service/db-crypto.cpp
+    ${KEY_MANAGER_PATH}/service/file-system.cpp
+    ${KEY_MANAGER_PATH}/dpl/core/src/assert.cpp
+    ${KEY_MANAGER_PATH}/dpl/db/src/sql_connection.cpp
+    ${KEY_MANAGER_PATH}/dpl/db/src/naive_synchronization_object.cpp
+    ${KEY_MANAGER_PATH}/sqlcipher/sqlcipher.c
 )
 
 INCLUDE_DIRECTORIES(SYSTEM ${ENCRYPTION_SCHEME_DEP_INCLUDE_DIRS})
-INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} )
+INCLUDE_DIRECTORIES(
+    ${CMAKE_CURRENT_SOURCE_DIR}
+
+    ${KEY_MANAGER_PATH}/common
+    ${KEY_MANAGER_PATH}/dpl/core/include
+    ${KEY_MANAGER_PATH}/dpl/log/include
+    ${KEY_MANAGER_PATH}/dpl/db/include
+    ${KEY_MANAGER_PATH}/sqlcipher
+    ${KEY_MANAGER_PATH}/service
+    ${KEY_MANAGER_PATH}/crypto
+)
 
 ADD_LIBRARY(${TARGET_ENCRYPTION_SCHEME_COMMON} STATIC ${ENCRYPTION_SCHEME_SOURCES})
 
@@ -38,6 +61,9 @@ TARGET_LINK_LIBRARIES(${TARGET_ENCRYPTION_SCHEME_COMMON}
     ${ENCRYPTION_SCHEME_DEP_LIBRARIES}
     ${TARGET_KEY_MANAGER_CLIENT}
     ${TARGET_KEY_MANAGER_CONTROL_CLIENT}
+    ${CMAKE_THREAD_LIBS_INIT}
+    boost_unit_test_framework
+    -ldl
 )
 
 INSTALL(TARGETS ${TARGET_ENCRYPTION_SCHEME_COMMON} DESTINATION ${LIB_INSTALL_DIR})
index 5422bd2..102bb69 100644 (file)
 
 #include <sys/smack.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
+#include <fcntl.h>
 #include <unistd.h>
+#include <string.h>
 
 #include <fstream>
 #include <stdexcept>
 
+#include <boost/test/unit_test.hpp>
+
 #include <smack-access.h>
 
+#include <db-crypto.h>
+#include <file-system.h>
+#include <key-provider.h>
+#include <db-row.h>
+#include <crypto-init.h>
+
 using namespace CKM;
 using namespace std;
 
@@ -37,9 +49,13 @@ const uid_t UID = 7654;
 const gid_t GID = 7654;
 const char* const DBPASS = "db-pass";
 const char* const LABEL = "my-label";
+const Label DB_LABEL = "/" + string(LABEL);
+const int ENC_SCHEME_OFFSET = 24;
 const string TEST_DATA_STR = "test-data";
 RawBuffer TEST_DATA(TEST_DATA_STR.begin(), TEST_DATA_STR.end());
 const Password TEST_PASS = "custom user password";
+const size_t IV_LEN = 16;
+const size_t CHAIN_LEN = 3;
 
 enum {
     NO_PASS = 0,
@@ -59,9 +75,9 @@ Policy policy[2][2] = {
 
 struct Group {
     enum {
-        KEY_PAIR,
-        CERT_CHAIN,
-        SINGLE_ITEM
+        SINGLE_ITEM,
+        KEY_PAIR_RSA,
+        CERT_CHAIN
     } type;
     Items items;
 };
@@ -76,24 +92,24 @@ Group GROUPS[] = {
     }},
 
     // RSA keys
-    { Group::KEY_PAIR, {
+    { Group::KEY_PAIR_RSA, {
             Item("key-rsa-alias-prv1", DataType::KEY_RSA_PRIVATE,  policy[NO_PASS][NO_EXP]),
             Item("key-rsa-alias-pub1", DataType::KEY_RSA_PUBLIC,   policy[NO_PASS][NO_EXP])
     }},
-    { Group::KEY_PAIR, {
+    { Group::KEY_PAIR_RSA, {
             Item("key-rsa-alias-prv2", DataType::KEY_RSA_PRIVATE,  policy[NO_PASS][EXP]),
             Item("key-rsa-alias-pub2", DataType::KEY_RSA_PUBLIC,   policy[NO_PASS][EXP]),
     }},
-    { Group::KEY_PAIR, {
+    { Group::KEY_PAIR_RSA, {
             Item("key-rsa-alias-prv3", DataType::KEY_RSA_PRIVATE,  policy[PASS][NO_EXP]),
             Item("key-rsa-alias-pub3", DataType::KEY_RSA_PUBLIC,   policy[PASS][NO_EXP]),
     }},
-    { Group::KEY_PAIR, {
+    { Group::KEY_PAIR_RSA, {
             Item("key-rsa-alias-prv4", DataType::KEY_RSA_PRIVATE,  policy[PASS][EXP]),
             Item("key-rsa-alias-pub4", DataType::KEY_RSA_PUBLIC,   policy[PASS][EXP]),
     }},
     // different policies
-    { Group::KEY_PAIR, {
+    { Group::KEY_PAIR_RSA, {
             Item("key-rsa-alias-prv5", DataType::KEY_RSA_PRIVATE,  policy[PASS][NO_EXP]),
             Item("key-rsa-alias-pub5", DataType::KEY_RSA_PUBLIC,   policy[NO_PASS][EXP]),
     }},
@@ -223,12 +239,71 @@ std::string TEST_LEAF =
     "Zj/T1JkYXKkEwZU6nAR2jdZp3EP9xj3o15V/tyFcXHx6l8NTxn4cJb+Xe4VquQJz\n"
     "6ON7PVe0ABN/AlwVQiFE\n"
     "-----END CERTIFICATE-----\n";
+
+
+
+struct FdCloser {
+    void operator()(int* fd) {
+        if(fd)
+            close(*fd);
+    }
+};
+
+typedef std::unique_ptr<int, FdCloser> FdPtr;
+
+void restoreFile(const string& filename) {
+    string sourcePath = "/usr/share/ckm-db-test/" + filename;
+    string targetPath = "/opt/data/ckm/" + filename;
+
+    int ret;
+
+    int sourceFd = TEMP_FAILURE_RETRY(open(sourcePath.c_str(), O_RDONLY));
+    BOOST_REQUIRE_MESSAGE(sourceFd > 0, "Opening " << sourcePath << " failed.");
+
+    FdPtr sourceFdPtr(&sourceFd);
+
+    int targetFd = TEMP_FAILURE_RETRY(creat(targetPath.c_str(), 666));
+    BOOST_REQUIRE_MESSAGE(targetFd > 0, "Creating " << targetPath << " failed.");
+
+    FdPtr targetFdPtr(&targetFd);
+
+    struct stat sourceStat;
+    ret = fstat(sourceFd, &sourceStat);
+    BOOST_REQUIRE_MESSAGE(ret != -1, "fstat() failed: " << ret);
+
+    ret = sendfile(targetFd, sourceFd, 0, sourceStat.st_size);
+    BOOST_REQUIRE_MESSAGE(ret != -1, "sendfile failed: " << ret);
+
+    ret = fsync(targetFd);
+    BOOST_REQUIRE_MESSAGE(ret != -1, "fsync failed: " << ret);
+}
+
+void generateRandom(size_t random_bytes, unsigned char *output)
+{
+    if(random_bytes<=0 || !output)
+        throw runtime_error("Invalid param");
+
+    std::ifstream is("/dev/urandom", std::ifstream::binary);
+    if(!is)
+        throw runtime_error("Failed to read /dev/urandom");
+    is.read(reinterpret_cast<char*>(output), random_bytes);
+    if(static_cast<std::streamsize>(random_bytes) != is.gcount())
+        throw runtime_error("Not enough bytes read from /dev/urandom");
+}
+
+RawBuffer createRandomBuffer(size_t random_bytes)
+{
+    RawBuffer buffer(random_bytes);
+    generateRandom(buffer.size(), buffer.data());
+    return buffer;
+}
 } // namespace anonymous
 
 
-SchemeTest::SchemeTest() : m_userChanged(false) {
+SchemeTest::SchemeTest() : m_userChanged(false), m_directAccessEnabled(false) {
     m_control = Control::create();
     m_mgr = Manager::create();
+    initOpenSsl();
 
     SmackAccess sa;
     sa.add("System", LABEL, "rwx");
@@ -243,6 +318,14 @@ SchemeTest::~SchemeTest() {
     } catch (...) {}
 }
 
+void SchemeTest::RemoveUserData() {
+    if(CKM_API_SUCCESS != m_control->lockUserKey(UID))
+        throw runtime_error("lockUserKey failed");
+
+    if(CKM_API_SUCCESS != m_control->removeUserData(UID))
+        throw runtime_error("removeUserData failed");
+}
+
 void SchemeTest::SwitchToUser() {
     if (m_userChanged)
         return;
@@ -309,7 +392,7 @@ void SchemeTest::FillDb() {
 
     for(const auto& g:GROUPS) {
         switch (g.type) {
-        case Group::KEY_PAIR:
+        case Group::KEY_PAIR_RSA:
             if(g.items.size() != 2)
                 throw runtime_error("Wrong number of keys");
             if( g.items[0].type != DataType::KEY_RSA_PRIVATE ||
@@ -366,3 +449,306 @@ void SchemeTest::FillDb() {
         }
     }
 }
+
+void SchemeTest::ReadAll(bool useWrongPass) {
+    SwitchToUser();
+
+    for(const auto& g:GROUPS) {
+        for(const auto& i:g.items) {
+            int ret;
+            Password pass = i.policy.password;
+            if(useWrongPass) {
+                if(pass.empty())
+                    pass = TEST_PASS;
+                else
+                    pass = Password();
+            }
+
+            switch (i.type) {
+            case DataType::BINARY_DATA:
+            {
+                RawBuffer receivedData;
+                ret = m_mgr->getData(i.alias, pass, receivedData);
+                BOOST_REQUIRE_MESSAGE(useWrongPass || receivedData == TEST_DATA,
+                                      "Received data is different for " << i.alias);
+                break;
+            }
+
+            case DataType::KEY_AES:
+            case DataType::KEY_RSA_PRIVATE:
+            case DataType::KEY_RSA_PUBLIC:
+            {
+                KeyShPtr receivedKey;
+                ret = m_mgr->getKey(i.alias, pass, receivedKey);
+                break;
+            }
+
+            case DataType::CERTIFICATE:
+            {
+                CertificateShPtr receivedCert;
+                ret = m_mgr->getCertificate(i.alias, pass, receivedCert);
+                break;
+            }
+
+            case DataType::CHAIN_CERT_0: // pkcs
+            {
+                PKCS12ShPtr pkcs;
+                ret = m_mgr->getPKCS12(i.alias, pass, pass, pkcs);
+                break;
+            }
+
+            default:
+                BOOST_FAIL("Unsupported data type " << i.type);
+            }
+
+            if(i.policy.extractable) {
+                if(useWrongPass)
+                    BOOST_REQUIRE_MESSAGE(ret == CKM_API_ERROR_AUTHENTICATION_FAILED,
+                                          "Reading item " << i.alias << " should fail with " <<
+                                          CKM_API_ERROR_AUTHENTICATION_FAILED << " got: " << ret);
+                else
+                    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "Reading item " << i.alias <<
+                                          " failed with " << ret);
+            }
+            else
+                BOOST_REQUIRE_MESSAGE(ret == CKM_API_ERROR_NOT_EXPORTABLE, "Item " << i.alias <<
+                                      " should not be exportable");
+        }
+    }
+}
+
+void SchemeTest::SignVerify() {
+    SwitchToUser();
+
+    for(const auto& g:GROUPS) {
+        if(g.type == Group::KEY_PAIR_RSA) {
+            BOOST_REQUIRE_MESSAGE(g.items.size() == 2, "Wrong number of keys");
+            BOOST_REQUIRE_MESSAGE(g.items[0].type == DataType::KEY_RSA_PRIVATE &&
+                                  g.items[1].type == DataType::KEY_RSA_PUBLIC, "Wrong key");
+
+            SignVerifyItem(g.items[0], g.items[1]);
+        } else {
+            for(const auto& i:g.items) {
+                switch (i.type) {
+                case DataType::CHAIN_CERT_0:
+                    SignVerifyItem(i, i);
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void SchemeTest::EncryptDecrypt() {
+    SwitchToUser();
+
+    for(const auto& g:GROUPS) {
+        if(g.type == Group::KEY_PAIR_RSA) {
+            BOOST_REQUIRE_MESSAGE(g.items.size() == 2, "Wrong number of keys");
+            BOOST_REQUIRE_MESSAGE(g.items[0].type == DataType::KEY_RSA_PRIVATE &&
+                                  g.items[1].type == DataType::KEY_RSA_PUBLIC, "Wrong key");
+
+            EncryptDecryptItem(g.items[0], g.items[1]);
+        } else {
+            for(const auto& i:g.items) {
+                switch (i.type) {
+                case DataType::KEY_AES:
+                    EncryptDecryptItem(i);
+                    break;
+
+                case DataType::CHAIN_CERT_0:
+                    EncryptDecryptItem(i, i);
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void SchemeTest::CreateChain() {
+    SwitchToUser();
+
+    for(const auto& g:GROUPS) {
+        if(g.type == Group::CERT_CHAIN) {
+            BOOST_REQUIRE_MESSAGE(g.items.size() == CHAIN_SIZE, "Not enough certificates");
+            for(const auto& c:g.items)
+                BOOST_REQUIRE_MESSAGE(c.type == DataType::CERTIFICATE, "Wrong item type");
+            Items trusted(CHAIN_SIZE-1);
+            std::copy(g.items.begin(), g.items.begin() + CHAIN_SIZE-1, trusted.begin());
+
+            // last one is ee (leaf)
+            CreateChainItem(g.items.back(), trusted);
+        } else {
+            for(const auto& i:g.items) {
+                if(i.type == DataType::CHAIN_CERT_0) // PKCS
+                    CreateChainItem(i, { i });
+            }
+        }
+    }
+}
+
+void SchemeTest::RemoveAll() {
+    SwitchToUser();
+
+    for(const auto& g:GROUPS) {
+        for(const auto& i:g.items) {
+            int ret = m_mgr->removeAlias(i.alias);
+            BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS,
+                                  "removeAlias() failed with " << ret << " for " << i.alias);
+        }
+    }
+}
+size_t SchemeTest::CountObjects() {
+    EnableDirectDbAccess();
+
+    size_t ret = 0;
+    for(const auto& g:GROUPS) {
+        for(const auto& i:g.items) {
+            DB::RowVector rows;
+            // it is assumed that aliases are different
+            m_db->getRows(i.alias, DB_LABEL, DataType::DB_FIRST, DataType::DB_LAST, rows);
+            ret += rows.size();
+        }
+    }
+    return ret;
+}
+
+void SchemeTest::RestoreDb() {
+    restoreFile("key-7654");
+    restoreFile("db-key-7654");
+    restoreFile("db-7654");
+    m_db.reset();
+    m_directAccessEnabled = false;
+}
+
+void SchemeTest::CheckSchemeVersion(const ItemFilter& filter, int version) {
+    EnableDirectDbAccess();
+
+    for(const auto& g:GROUPS) {
+        for(const auto& i:g.items) {
+            if(!filter.Matches(i))
+                continue;
+
+            DB::RowVector rows;
+            m_db->getRows(i.alias, DB_LABEL, filter.typeFrom, filter.typeTo, rows);
+            BOOST_REQUIRE_MESSAGE(rows.size() > 0, "No rows found for " << i.alias);
+            for(const auto& r : rows) {
+                BOOST_REQUIRE_MESSAGE(
+                        (r.encryptionScheme >> ENC_SCHEME_OFFSET) == version,
+                        "Wrong encryption scheme for " << i.alias << ". Expected " << version <<
+                        " got: " << (r.encryptionScheme >> ENC_SCHEME_OFFSET));
+            }
+        }
+    }
+}
+
+void SchemeTest::EnableDirectDbAccess() {
+    SwitchToRoot();
+
+    if(m_directAccessEnabled)
+        return;
+
+    // direct access to db
+    FileSystem fs(UID);
+    auto wrappedDKEK = fs.getDKEK();
+    auto keyProvider = KeyProvider(wrappedDKEK, DBPASS);
+
+    auto wrappedDatabaseDEK = fs.getDBDEK();
+    RawBuffer key = keyProvider.getPureDEK(wrappedDatabaseDEK);
+
+    m_db.reset(new DB::Crypto(fs.getDBPath(), key));
+    m_directAccessEnabled = true;
+}
+
+void SchemeTest::SignVerifyItem(const Item& itemPrv, const Item& itemPub) {
+    int ret;
+    KeyShPtr receivedKey;
+    RawBuffer signature;
+    // create/verify signature
+    ret = m_mgr->createSignature(itemPrv.alias,
+                                 itemPrv.policy.password,
+                                 TEST_DATA,
+                                 HashAlgorithm::SHA512,
+                                 RSAPaddingAlgorithm::X931,
+                                 signature);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "createSignature() failed with " << ret <<
+                          " for " << itemPrv.alias);
+    ret = m_mgr->verifySignature(itemPub.alias,
+                                 itemPub.policy.password,
+                                 TEST_DATA,
+                                 signature,
+                                 HashAlgorithm::SHA512,
+                                 RSAPaddingAlgorithm::X931);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "verifySignature() failed with " << ret <<
+                          " for " << itemPub.alias);
+
+}
+
+void SchemeTest::EncryptDecryptItem(const Item& item) {
+    CryptoAlgorithm algo;
+    RawBuffer iv = createRandomBuffer(IV_LEN);
+    RawBuffer encrypted, decrypted;
+    int ret;
+
+    algo.setParam(ParamName::ALGO_TYPE, AlgoType::AES_GCM);
+    algo.setParam(ParamName::ED_IV, iv);
+
+    ret = m_mgr->encrypt(algo, item.alias, item.policy.password, TEST_DATA, encrypted);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "encrypt() failed iwth " << ret << " for " <<
+                          item.alias);
+
+    ret = m_mgr->decrypt(algo, item.alias, item.policy.password, encrypted, decrypted);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "decrypt() failed iwth " << ret << " for " <<
+                          item.alias);
+
+    BOOST_REQUIRE_MESSAGE(decrypted == TEST_DATA, "Decrypted data not equal to original");
+}
+
+void SchemeTest::EncryptDecryptItem(const Item& itemPrv, const Item& itemPub) {
+    CryptoAlgorithm algo;
+    RawBuffer encrypted, decrypted;
+    int ret;
+
+    algo.setParam(ParamName::ALGO_TYPE, AlgoType::RSA_OAEP);
+
+    ret = m_mgr->encrypt(algo, itemPub.alias, itemPub.policy.password, TEST_DATA, encrypted);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "encrypt() failed iwth " << ret << " for " <<
+                          itemPub.alias);
+
+    ret = m_mgr->decrypt(algo, itemPrv.alias, itemPrv.policy.password, encrypted, decrypted);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "decrypt() failed iwth " << ret << " for " <<
+                          itemPrv.alias);
+
+    BOOST_REQUIRE_MESSAGE(decrypted == TEST_DATA, "Decrypted data not equal to original");
+}
+
+void SchemeTest::CreateChainItem(const Item& leaf, const Items& certs) {
+    CertificateShPtrVector chain;
+    AliasVector trusted;
+
+    if(!leaf.policy.extractable || !leaf.policy.password.empty())
+        return;
+
+    for(const auto& i : certs) {
+        if(!i.policy.extractable || !i.policy.password.empty())
+            return;
+        trusted.push_back(i.alias);
+    }
+
+    CertificateShPtr leafCrt;
+    int ret = m_mgr->getCertificate(leaf.alias, leaf.policy.password, leafCrt);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS,
+                          "getCertificate failed with " << ret << " for " <<
+                          leaf.alias);
+
+    ret = m_mgr->getCertificateChain(leafCrt, AliasVector(), trusted, false, chain);
+    BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS,
+                          "getCertificateChain() failed with " << ret);
+    BOOST_REQUIRE_MESSAGE(chain.size() == CHAIN_LEN, "Wrong chain length: " << chain.size());
+}
index 69edd24..6d020b7 100644 (file)
 
 #pragma once
 
+#include <memory>
 #include <string>
+#include <vector>
 
 #include <ckm/ckm-control.h>
 #include <ckm/ckm-manager.h>
 
 #include <data-type.h>
 
+namespace CKM {
+namespace DB {
+class Crypto;
+} // DB
+} // CKM
+
 struct Item {
-    Item() : type(CKM::DataType::DB_FIRST){}
+    Item() {}
     Item(const CKM::Alias& alias,
          const CKM::DataType::Type type,
          const CKM::Policy& policy)
@@ -44,19 +52,74 @@ struct Item {
 
 typedef std::vector<Item> Items;
 
+struct ItemFilter {
+    ItemFilter() :
+        typeFrom(CKM::DataType::DB_FIRST),
+        typeTo(CKM::DataType::DB_LAST),
+        exportableOnly(false),
+        noPassword(false)
+    {}
+
+    explicit ItemFilter(CKM::DataType::Type type) :
+        typeFrom(type),
+        typeTo(type),
+        exportableOnly(false),
+        noPassword(false)
+    {}
+
+    ItemFilter(CKM::DataType::Type typeFrom, CKM::DataType::Type typeTo) :
+        typeFrom(typeFrom),
+        typeTo(typeTo),
+        exportableOnly(false),
+        noPassword(false)
+    {}
+
+    bool Matches(const Item& item) const {
+        if(item.type < typeFrom || item.type > typeTo)
+            return false;
+        if(exportableOnly && !item.policy.extractable)
+            return false;
+        if(noPassword && !item.policy.password.empty())
+            return false;
+        return true;
+    }
+
+    CKM::DataType::Type typeFrom;
+    CKM::DataType::Type typeTo;
+    bool exportableOnly;
+    bool noPassword;
+};
+
 class SchemeTest {
 public:
     SchemeTest();
     ~SchemeTest();
 
+    void RemoveUserData();
     void FillDb();
+    void ReadAll(bool useWrongPass = false);
+    void SignVerify();
+    void EncryptDecrypt();
+    void CreateChain();
+    void RemoveAll();
+    size_t CountObjects();
+    void RestoreDb();
+    void CheckSchemeVersion(const ItemFilter& filter, int version);
 
 private:
     void SwitchToUser();
     void SwitchToRoot();
+    void EnableDirectDbAccess();
+    void SignVerifyItem(const Item& itemPrv, const Item& itemPub);
+    void EncryptDecryptItem(const Item& item);
+    void EncryptDecryptItem(const Item& itemPrv, const Item& itemPub);
+    void CreateChainItem(const Item& leaf, const Items& certs);
 
     CKM::ControlShPtr m_control;
     CKM::ManagerShPtr m_mgr;
     std::string m_origLabel;
     bool m_userChanged;
+
+    std::unique_ptr<CKM::DB::Crypto> m_db;
+    bool m_directAccessEnabled;
 };
diff --git a/tests/test_encryption-scheme.cpp b/tests/test_encryption-scheme.cpp
new file mode 100644 (file)
index 0000000..9e4579a
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd 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
+ */
+/*
+ * @file       test_encryption-scheme.cpp
+ * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
+ * @version    1.0
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/results_reporter.hpp>
+
+#include <scheme-test.h>
+
+using namespace CKM;
+
+namespace {
+// this is done to limit the amount of code included in binary
+const int OLD_ENC_SCHEME  = 0;
+const int NEW_ENC_SCHEME  = 1;
+} // namespace anonymous
+
+
+BOOST_AUTO_TEST_SUITE(ENCRYPTION_SCHEME_TEST)
+
+// Test database should have the old scheme
+BOOST_AUTO_TEST_CASE(T010_Check_old_scheme) {
+    SchemeTest test;
+    test.RestoreDb();
+
+    ItemFilter filter;
+    test.CheckSchemeVersion(filter, OLD_ENC_SCHEME);
+}
+
+// Newly written data should use the new scheme
+BOOST_AUTO_TEST_CASE(T020_Check_new_scheme) {
+    SchemeTest test;
+    test.RemoveUserData();
+    test.FillDb();
+
+    ItemFilter filter;
+    test.CheckSchemeVersion(filter, NEW_ENC_SCHEME);
+}
+
+BOOST_AUTO_TEST_CASE(T030_Remove_old_scheme) {
+    SchemeTest test;
+    test.RestoreDb();
+    test.RemoveAll();
+
+    size_t aliases = test.CountObjects();
+    BOOST_REQUIRE_MESSAGE(aliases == 0, "All aliases should be removed");
+}
+
+BOOST_AUTO_TEST_CASE(T040_Remove_new_scheme) {
+    SchemeTest test;
+    test.RemoveUserData();
+    test.FillDb();
+    test.RemoveAll();
+
+    size_t aliases = test.CountObjects();
+    BOOST_REQUIRE_MESSAGE(aliases == 0, "All aliases should be removed");
+}
+
+// Reading old db should reencrypt objects with new scheme
+BOOST_AUTO_TEST_CASE(T100_Read) {
+    SchemeTest test;
+    test.RestoreDb();
+    test.ReadAll();
+
+    ItemFilter filter;
+    filter.exportableOnly = true;
+    test.CheckSchemeVersion(filter, NEW_ENC_SCHEME);
+}
+
+BOOST_AUTO_TEST_CASE(T110_Count_objects_after_read) {
+    SchemeTest test;
+    test.RestoreDb();
+    size_t orig = test.CountObjects();
+    BOOST_REQUIRE_MESSAGE(orig > 0, "No objects in db");
+
+    test.ReadAll();
+
+    size_t current = test.CountObjects();
+    BOOST_REQUIRE_MESSAGE(current == orig,
+                          "Original number of objects: " << orig << " Current: " << current);
+}
+
+// Reading old db with incorrect passwords should leave the scheme unchanged
+BOOST_AUTO_TEST_CASE(T120_Read_wrong_pass) {
+    SchemeTest test;
+    test.RestoreDb();
+    test.ReadAll(true);
+
+    ItemFilter filter;
+    test.CheckSchemeVersion(filter, OLD_ENC_SCHEME);
+}
+
+// Signing/verification should reencrypt objects with new scheme
+BOOST_AUTO_TEST_CASE(T200_SignVerify) {
+    SchemeTest test;
+    test.RestoreDb();
+    test.SignVerify();
+
+    ItemFilter filter(DataType::KEY_RSA_PUBLIC, DataType::KEY_RSA_PRIVATE);
+    test.CheckSchemeVersion(filter, NEW_ENC_SCHEME);
+}
+
+// Encryption/decryption should reencrypt objects with new scheme
+BOOST_AUTO_TEST_CASE(T210_EncryptDecrypt) {
+    SchemeTest test;
+    test.RestoreDb();
+    test.EncryptDecrypt();
+
+    ItemFilter filter1(DataType::KEY_RSA_PUBLIC, DataType::KEY_RSA_PRIVATE);
+    test.CheckSchemeVersion(filter1, NEW_ENC_SCHEME);
+
+    ItemFilter filter2(DataType::KEY_AES);
+    test.CheckSchemeVersion(filter2, NEW_ENC_SCHEME);
+}
+
+// Chain creation should reencrypt objects with new scheme
+BOOST_AUTO_TEST_CASE(T220_CreateChain) {
+    SchemeTest test;
+    test.RestoreDb();
+    test.CreateChain();
+
+    // non exportable certificates and certificates protected with passwords can't be used for chain
+    // creation
+    ItemFilter filter1(DataType::CERTIFICATE);
+    filter1.exportableOnly = true;
+    filter1.noPassword = true;
+    test.CheckSchemeVersion(filter1, NEW_ENC_SCHEME);
+
+    ItemFilter filter2(DataType::CHAIN_CERT_0, DataType::CHAIN_CERT_15);
+    filter2.exportableOnly = true;
+    filter2.noPassword = true;
+    test.CheckSchemeVersion(filter2, NEW_ENC_SCHEME);
+}
+
+BOOST_AUTO_TEST_SUITE_END()