From: Krzysztof Jackiewicz Date: Thu, 4 Apr 2019 14:58:27 +0000 (+0200) Subject: CKM: Add sign/verify test for both backends X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1c94e60f57cbd5e987955ece417a00c762b32e3c;p=platform%2Fcore%2Ftest%2Fsecurity-tests.git CKM: Add sign/verify test for both backends Add a generic signing/verification test runnable on both backends. Change-Id: Ia0b646fd8cf1b256e82a5f12abf6c0940fca3c64 --- diff --git a/src/ckm/unprivileged/CMakeLists.txt b/src/ckm/unprivileged/CMakeLists.txt index 6efdfbe2..14a89735 100644 --- a/src/ckm/unprivileged/CMakeLists.txt +++ b/src/ckm/unprivileged/CMakeLists.txt @@ -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 index 00000000..c1a7b445 --- /dev/null +++ b/src/ckm/unprivileged/sign-verify.cpp @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include + +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 ALGO2STR = { + { RSA, "RSA" }, + { DSA, "DSA" }, + { ECDSA, "ECDSA" }, +}; + +// keys +std::unordered_map>> KEYS; + +enum KeyIdx { + PRIMARY = 0, + PASSWORD_PROTECTED = 1, + + KEY_IDX_MAX +}; + +// hash algo names +const std::unordered_map 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 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(ElipticCurve::prime192v1); +const int EC_PRIME256V1 = static_cast(ElipticCurve::prime256v1); +const int EC_SECP384R1 = static_cast(ElipticCurve::secp384r1); + +// test messages +RawBufferPtr MESSAGE_SHORT; +std::unordered_map 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(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