--- /dev/null
+/*
+ * 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