CKM: Add sign/verify test for both backends 94/203094/2
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 4 Apr 2019 14:58:27 +0000 (16:58 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 15 May 2019 15:55:00 +0000 (17:55 +0200)
Add a generic signing/verification test runnable on both backends.

Change-Id: Ia0b646fd8cf1b256e82a5f12abf6c0940fca3c64

src/ckm/unprivileged/CMakeLists.txt
src/ckm/unprivileged/sign-verify.cpp [new file with mode: 0644]

index 6efdfbe..14a8973 100644 (file)
@@ -23,6 +23,7 @@ SET(CKM_SOURCES
     capi-testcases.cpp
     encryption-decryption-env.cpp
     encryption-decryption.cpp
+    sign-verify.cpp
     main.cpp
 )
 
diff --git a/src/ckm/unprivileged/sign-verify.cpp b/src/ckm/unprivileged/sign-verify.cpp
new file mode 100644 (file)
index 0000000..c1a7b44
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ *  Copyright (c) 2019 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       sign-verify.cpp
+ * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
+ * @version    1.0
+ */
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include <dpl/test/test_runner.h>
+#include <ckm-common.h>
+#include <ckmc/ckmc-manager.h>
+#include <ckmc/ckmc-control.h>
+#include <ckm/ckm-type.h>
+#include <ckm/ckm-manager.h>
+
+using namespace CKM;
+
+namespace {
+
+const char* PASSWORD = "test-password";
+const uid_t UID = 5001;
+
+struct KeyAliasPair
+{
+    Alias prv;
+    Alias pub;
+};
+
+enum Algo {
+    RSA,
+    DSA,
+    ECDSA
+};
+
+// algo names
+const std::unordered_map<Algo, std::string> ALGO2STR = {
+        { RSA,   "RSA" },
+        { DSA,   "DSA" },
+        { ECDSA, "ECDSA" },
+};
+
+// keys
+std::unordered_map<Algo, std::unordered_map<size_t, std::vector<KeyAliasPair>>> KEYS;
+
+enum KeyIdx {
+    PRIMARY = 0,
+    PASSWORD_PROTECTED = 1,
+
+    KEY_IDX_MAX
+};
+
+// hash algo names
+const std::unordered_map<ckmc_hash_algo_e, std::string> HASH2STR = {
+#ifndef TZ_BACKEND
+        // no hash is not supported in TZ
+        { CKMC_HASH_NONE,   "NONE" },
+#endif
+        { CKMC_HASH_SHA1,   "SHA1" },
+        { CKMC_HASH_SHA256, "SHA256" },
+        { CKMC_HASH_SHA384, "SHA384" },
+        { CKMC_HASH_SHA512, "SHA512" },
+};
+
+// padding names
+const std::unordered_map<ckmc_rsa_padding_algo_e, std::string> PAD2STR = {
+#ifndef TZ_BACKEND
+        // no padding is not supported in TZ
+        { CKMC_NONE_PADDING,    "NONE" },
+#endif
+        { CKMC_PKCS1_PADDING,   "PKCS1" },
+#ifndef TZ_BACKEND
+        // X9.31 is not supported in TZ
+        { CKMC_X931_PADDING,    "X931" },
+#endif
+};
+
+const int EC_PRIME192V1 = static_cast<int>(ElipticCurve::prime192v1);
+const int EC_PRIME256V1 = static_cast<int>(ElipticCurve::prime256v1);
+const int EC_SECP384R1 = static_cast<int>(ElipticCurve::secp384r1);
+
+// test  messages
+RawBufferPtr MESSAGE_SHORT;
+std::unordered_map<size_t, RawBufferPtr> MESSAGES;
+RawBufferPtr MESSAGE_LONG;
+
+class SignVerifyGroupFixture: public DPL::Test::TestGroup
+{
+public:
+    void Init() override
+    {
+        remove_user_data(UID);
+        int ret = ckmc_unlock_user_key(UID, "db-pass");
+        if (ret != CKMC_ERROR_NONE)
+            RUNNER_ERROR_MSG("DB unlock failed: " << CKMCErrorToString(ret));
+
+        // Policy backend to use in subsequent operations (global for each test case)
+#ifdef TZ_BACKEND
+        m_backend = PolicyBackend::FORCE_HARDWARE;
+#else
+        m_backend = PolicyBackend::FORCE_SOFTWARE;
+#endif
+
+        // generate keys
+        m_manager = Manager::create();
+        generateKeys(RSA, 1024);
+        generateKeys(RSA, 2048);
+        generateKeys(RSA, 4096);
+        generateKeys(DSA, 1024);
+#ifndef TZ_BACKEND
+        /*
+         * For DSA with SHA1 only 1024-bit keys are supported and TZ does not currently support
+         * anything else than SHA1 for DSA.
+         */
+        generateKeys(DSA, 2048);
+        generateKeys(DSA, 3072);
+        generateKeys(DSA, 4096);
+#endif
+#ifndef TZ_BACKEND
+        // ECDSA is not yet supported on TZ
+        generateKeys(ECDSA, EC_PRIME192V1);
+        generateKeys(ECDSA, EC_PRIME256V1);
+        generateKeys(ECDSA, EC_SECP384R1);
+#endif
+
+        MESSAGE_SHORT = create_raw_buffer(createRandomBufferCAPI(512/8));
+
+        // Set first byte to 0 to avoid "data too large for modulus" error in unpadded RSA
+        MESSAGES[1024] = create_raw_buffer(createRandomBufferCAPI(1024/8));
+        MESSAGES[1024]->data[0] = 0;
+        MESSAGES[2048] = create_raw_buffer(createRandomBufferCAPI(2048/8));
+        MESSAGES[2048]->data[0] = 0;
+        MESSAGES[3072] = create_raw_buffer(createRandomBufferCAPI(3072/8));
+        MESSAGES[3072]->data[0] = 0;
+        MESSAGES[4096]= create_raw_buffer(createRandomBufferCAPI(4096/8));
+        MESSAGES[4096]->data[0] = 0;
+
+        MESSAGE_LONG = create_raw_buffer(createRandomBufferCAPI(1000));
+    }
+
+    void generateKeys(Algo type, size_t bitLen)
+    {
+        for (int i = 0; i < KEY_IDX_MAX; i++)
+        {
+            Policy prvPolicy(Password(), false, m_backend);
+            Policy pubPolicy(Password(), true, m_backend);
+            if (i == PASSWORD_PROTECTED) {
+                prvPolicy.password.assign(PASSWORD);
+                pubPolicy.password.assign(PASSWORD);
+            }
+
+            KeyAliasPair alias;
+            alias.prv = ALGO2STR.at(type) + std::string("_") + std::to_string(bitLen) +
+                        std::string("_") + std::to_string(i);
+            alias.pub = std::string("pub") + alias.prv;
+            int ret;
+            switch (type)
+            {
+            case RSA:
+                ret = m_manager->createKeyPairRSA(bitLen,
+                                                  alias.prv, alias.pub,
+                                                  prvPolicy, pubPolicy);
+                break;
+            case DSA:
+                ret = m_manager->createKeyPairDSA(bitLen,
+                                                  alias.prv, alias.pub,
+                                                  prvPolicy, pubPolicy);
+                break;
+            case ECDSA:
+                ret = m_manager->createKeyPairECDSA(static_cast<ElipticCurve>(bitLen),
+                                                    alias.prv, alias.pub,
+                                                    prvPolicy, pubPolicy);
+                break;
+            default:
+                ret = CKM_API_ERROR_UNKNOWN;
+            }
+            if (ret != CKM_API_SUCCESS)
+                RUNNER_ERROR_MSG("key creation failed. Type: " << ALGO2STR.at(type) <<
+                                 " bits: " << bitLen << " error: " <<  APICodeToString(ret));
+
+            KEYS[type][bitLen].push_back(alias);
+        }
+    }
+
+    void Finish() override
+    {
+        for (const auto &type : KEYS) {
+            for (const auto &entry : type.second) {
+                for (const auto &keyPair : entry.second) {
+                    m_manager->removeAlias(keyPair.prv);
+                    m_manager->removeAlias(keyPair.pub);
+                }
+            }
+        }
+
+        MESSAGE_SHORT.reset();
+
+        int ret = ckmc_lock_user_key(UID);
+        if (ret != CKMC_ERROR_NONE)
+            RUNNER_ERROR_MSG("DB lock failed: " << CKMCErrorToString(ret));
+        remove_user_data(UID);
+    }
+private:
+    ManagerShPtr m_manager;
+    PolicyBackend m_backend;
+};
+
+std::string params2str(const Alias& alias,
+                       const ckmc_hash_algo_e hash,
+                       const ckmc_rsa_padding_algo_e padding,
+                       const RawBufferPtr& message)
+{
+    std::stringstream ss;
+    ss << " Alias: " <<  alias << ", hash algo: " << HASH2STR.at(hash) <<
+          ", padding: " << PAD2STR.at(padding) << ", message len: " << message->size << "B.";
+    return ss.str();
+}
+
+void signExpect(int expected,
+                const Alias& alias,
+                const char* pw,
+                const RawBufferPtr& message,
+                const ckmc_hash_algo_e hash,
+                const ckmc_rsa_padding_algo_e padding,
+                RawBufferPtr& signature)
+{
+    ckmc_raw_buffer_s* cSignature = nullptr;
+
+    int ret = ckmc_create_signature(alias.c_str(), pw, *message, hash, padding, &cSignature);
+    RUNNER_ASSERT_MSG(ret == expected, "Unexpected result during signature creation." <<
+                                       params2str(alias, hash, padding, message) <<
+                                       " Expected: " << CKMCErrorToString(expected) <<
+                                       " got: " << CKMCErrorToString(ret));
+    if (ret == CKMC_ERROR_NONE) {
+        RUNNER_ASSERT_MSG(cSignature != nullptr && cSignature->size > 0,
+                          "Empty signature returned." <<
+                          params2str(alias, hash, padding, message));
+        signature = create_raw_buffer(cSignature);
+    } else {
+        RUNNER_ASSERT_MSG(cSignature == nullptr,
+                          "Non-empty signature returned." <<
+                          params2str(alias, hash, padding, message));
+    }
+}
+
+void signInvalid(const Alias& alias,
+                 const char* pw,
+                 const RawBufferPtr& message,
+                 const ckmc_hash_algo_e hash,
+                 const ckmc_rsa_padding_algo_e padding)
+{
+    RawBufferPtr signature;
+    signExpect(CKMC_ERROR_INVALID_PARAMETER, alias, pw, message, hash, padding, signature);
+}
+
+void verifyExpect(int expected,
+                  const Alias& alias,
+                  const char* pw,
+                  const RawBufferPtr& message,
+                  const ckmc_hash_algo_e hash,
+                  const ckmc_rsa_padding_algo_e padding,
+                  const RawBufferPtr& signature)
+{
+    int ret = ckmc_verify_signature(alias.c_str(), pw, *message, *signature, hash, padding);
+    RUNNER_ASSERT_MSG(ret == expected, "Unexpected result during signature verification." <<
+                                       params2str(alias, hash, padding, message) <<
+                                       " Expected: " << CKMCErrorToString(expected) <<
+                                       " got: " << CKMCErrorToString(ret));
+}
+
+void signVerify(const KeyAliasPair& aliasPair,
+                const char* pw,
+                const RawBufferPtr& message,
+                const ckmc_hash_algo_e hash,
+                const ckmc_rsa_padding_algo_e padding)
+{
+    RawBufferPtr signature;
+    signExpect(CKMC_ERROR_NONE, aliasPair.prv, pw, message, hash, padding, signature);
+
+    RUNNER_ASSERT_MSG(signature->size > 0, "Empty signature returned");
+
+    verifyExpect(CKMC_ERROR_NONE, aliasPair.pub, pw, message, hash, padding, signature);
+
+    // modify 1 bit of the signature
+    signature->data[0] ^= 0x01;
+
+    // expect verification failure
+    verifyExpect(CKMC_ERROR_VERIFICATION_FAILED,
+                 aliasPair.pub,
+                 pw,
+                 message,
+                 hash,
+                 padding,
+                 signature);
+}
+// test given key pair against all hash and padding algos
+void testSignVerify(Algo algo, size_t keyBits, int idx)
+{
+#ifdef TZ_BACKEND
+    if (algo == DSA && keyBits > 1024)
+        RUNNER_IGNORED_MSG("For DSA with SHA1 only 1024-bit keys are supported and TZ does not"\
+                           " currently support anything else than SHA1 for DSA.");
+    if (algo == ECDSA)
+        RUNNER_IGNORED_MSG("ECDSA is not yet supported in TZ");
+#endif
+
+    std::string pw;
+    if (idx == PASSWORD_PROTECTED)
+        pw = PASSWORD;
+
+    const KeyAliasPair& keys = KEYS[algo][keyBits][idx];
+
+    // iterate over hash algorithms
+    for (const auto& hash : HASH2STR) {
+#ifdef TZ_BACKEND
+        // in case of DSA only SHA1 is supported on TZ
+        if (algo == DSA && hash.first != CKMC_HASH_SHA1)
+            continue;
+#endif
+
+        // iterate over padding algorithms
+        for (const auto& pad : PAD2STR) {
+            auto expectSuccess = [&](const RawBufferPtr& msg) {
+                signVerify(keys, pw.c_str(), msg, hash.first, pad.first);
+            };
+            auto expectInvalid = [&](const RawBufferPtr& msg) {
+                signInvalid(keys.prv, pw.c_str(), msg, hash.first, pad.first);
+            };
+
+            // padding is for RSA only, other algos should ignore it
+            if (algo == RSA && pad.first == CKMC_NONE_PADDING) {
+                if (hash.first == CKMC_HASH_NONE) {
+                    // no hash + no padding + key matching message
+                    expectSuccess(MESSAGES.at(keyBits));
+                }
+                // no padding + short message
+                expectInvalid(MESSAGE_SHORT);
+
+                // no padding + long message
+                expectInvalid(MESSAGE_LONG);
+
+            } else {
+                if (hash.first == CKMC_HASH_NONE) {
+                    // no hash + padding + long message
+                    expectInvalid(MESSAGE_LONG);
+
+                    // no hash + padding + short message
+                    if (algo == RSA)
+                        expectSuccess(MESSAGE_SHORT);
+                    else
+                        expectInvalid(MESSAGE_SHORT); // no support for CKMC_HASH_NONE
+                } else {
+                    // hash + padding + short message
+                    expectSuccess(MESSAGE_SHORT);
+
+                    // hash + padding + long message
+                    expectSuccess(MESSAGE_LONG);
+                }
+            }
+        }
+    }
+}
+
+} // namespace anonymous
+
+RUNNER_TEST_GROUP_INIT_ENV(CKM_SIGN_VERIFY, SignVerifyGroupFixture);
+
+
+// RSA
+RUNNER_TEST(TSV_0110_sign_verify_rsa_1024)
+{
+    testSignVerify(RSA, 1024, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0120_sign_verify_rsa_1024_pw)
+{
+    testSignVerify(RSA, 1024, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0130_sign_verify_rsa_2048)
+{
+    testSignVerify(RSA, 2048, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0140_sign_verify_rsa_2048_pw)
+{
+    testSignVerify(RSA, 2048, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0150_sign_verify_rsa_4096)
+{
+    testSignVerify(RSA, 4096, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0160_sign_verify_rsa_4096_pw)
+{
+    testSignVerify(RSA, 4096, PASSWORD_PROTECTED);
+}
+
+
+// DSA
+RUNNER_TEST(TSV_0210_sign_verify_dsa_1024)
+{
+    testSignVerify(DSA, 1024, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0220_sign_verify_dsa_1024_pw)
+{
+    testSignVerify(DSA, 1024, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0230_sign_verify_dsa_2048)
+{
+    testSignVerify(DSA, 2048, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0240_sign_verify_dsa_2048_pw)
+{
+    testSignVerify(DSA, 2048, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0250_sign_verify_dsa_3072)
+{
+    testSignVerify(DSA, 3072, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0260_sign_verify_dsa_3072_pw)
+{
+    testSignVerify(DSA, 3072, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0270_sign_verify_dsa_4096)
+{
+    testSignVerify(DSA, 4096, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0280_sign_verify_dsa_4096_pw)
+{
+    testSignVerify(DSA, 4096, PASSWORD_PROTECTED);
+}
+
+
+// ECDSA
+RUNNER_TEST(TSV_0310_sign_verify_ecdsa_PRIME192V1)
+{
+    testSignVerify(ECDSA, EC_PRIME192V1, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0320_sign_verify_ecdsa_PRIME192V1_pw)
+{
+    testSignVerify(ECDSA, EC_PRIME192V1, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0330_sign_verify_ecdsa_PRIME256V1)
+{
+    testSignVerify(ECDSA, EC_PRIME256V1, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0340_sign_verify_ecdsa_PRIME256V1_pw)
+{
+    testSignVerify(ECDSA, EC_PRIME256V1, PASSWORD_PROTECTED);
+}
+
+RUNNER_TEST(TSV_0350_sign_verify_ecdsa_SECP384R1)
+{
+    testSignVerify(ECDSA, EC_SECP384R1, PRIMARY);
+}
+
+RUNNER_TEST(TSV_0360_sign_verify_ecdsa_SECP384R1_pw)
+{
+    testSignVerify(ECDSA, EC_SECP384R1, PASSWORD_PROTECTED);
+}
+
+// TODO: border cases for padding
+// TODO: invalid arguments
+// TODO: Big data