From 2bfa145a913315ec91142d21411e4328f03631cc Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Tue, 6 Oct 2015 12:41:38 +0200 Subject: [PATCH 01/16] Remove warnings and performance problems reported by cppcheck. Change-Id: I6c39ff383a19554da5e9f875db51864e0e5941d0 --- src/manager/client-capi/ckmc-manager.cpp | 8 +++----- src/manager/client-capi/ckmc-type.cpp | 6 +++--- src/manager/common/base64.cpp | 4 ++-- src/manager/crypto/sw-backend/internals.cpp | 9 +++------ src/manager/service/ckm-service.cpp | 1 - src/manager/service/ocsp.cpp | 4 ++-- tests/test_xml-parser.cpp | 2 +- tools/ckm_tool.cpp | 2 +- 8 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/manager/client-capi/ckmc-manager.cpp b/src/manager/client-capi/ckmc-manager.cpp index dd5c644..c998171 100644 --- a/src/manager/client-capi/ckmc-manager.cpp +++ b/src/manager/client-capi/ckmc-manager.cpp @@ -94,9 +94,8 @@ ckmc_cert_list_s *_toNewCkmCertList(const CKM::CertificateShPtrVector &certVecto int ret; ckmc_cert_list_s *start = NULL; ckmc_cert_list_s *plist = NULL; - CKM::CertificateShPtrVector::const_iterator it; - for(it = certVector.begin(); it != certVector.end(); it++) { - CKM::RawBuffer rawBuffer = (*it)->getDER(); + for(const auto &e : certVector) { + CKM::RawBuffer rawBuffer = e->getDER(); ckmc_cert_s *pcert = NULL; ret = ckmc_cert_new(rawBuffer.data(), rawBuffer.size(), CKMC_FORM_DER, &pcert); if(pcert == NULL) { @@ -821,12 +820,11 @@ int ckmc_ocsp_check(const ckmc_cert_list_s *pcert_chain_list, ckmc_ocsp_status_e return CKMC_ERROR_INVALID_PARAMETER; } - int ret = CKMC_ERROR_UNKNOWN; int tmpOcspStatus = -1; CKM::ManagerShPtr mgr = CKM::Manager::create(); CKM::CertificateShPtrVector ckmCertChain = _toCkmCertificateVector(pcert_chain_list); - ret = mgr->ocspCheck(ckmCertChain, tmpOcspStatus); + int ret = mgr->ocspCheck(ckmCertChain, tmpOcspStatus); *ocsp_status = to_ckmc_ocsp_status(tmpOcspStatus); return to_ckmc_error(ret); } diff --git a/src/manager/client-capi/ckmc-type.cpp b/src/manager/client-capi/ckmc-type.cpp index 57071f0..7ce34be 100644 --- a/src/manager/client-capi/ckmc-type.cpp +++ b/src/manager/client-capi/ckmc-type.cpp @@ -75,7 +75,7 @@ int ckmc_key_new(unsigned char *raw_key, size_t key_size, ckmc_key_type_e key_ty { ckmc_key_s *pkey; - if(raw_key == NULL || key_size <= 0 || ppkey == NULL) { + if(raw_key == NULL || key_size == 0 || ppkey == NULL) { return CKMC_ERROR_INVALID_PARAMETER; } @@ -132,7 +132,7 @@ int ckmc_buffer_new(unsigned char *data, size_t size,ckmc_raw_buffer_s **ppbuffe { ckmc_raw_buffer_s *pbuff; - if(data == NULL || size <= 0 || ppbuffer == NULL) { + if(data == NULL || size == 0 || ppbuffer == NULL) { return CKMC_ERROR_INVALID_PARAMETER; } @@ -171,7 +171,7 @@ int ckmc_cert_new(unsigned char *raw_cert, size_t cert_size, ckmc_data_format_e { ckmc_cert_s *pcert; - if(raw_cert == NULL || cert_size <= 0 || ppcert == NULL) { + if(raw_cert == NULL || cert_size == 0 || ppcert == NULL) { return CKMC_ERROR_INVALID_PARAMETER; } diff --git a/src/manager/common/base64.cpp b/src/manager/common/base64.cpp index f184505..ad94d4b 100644 --- a/src/manager/common/base64.cpp +++ b/src/manager/common/base64.cpp @@ -63,9 +63,9 @@ RawBuffer Base64Encoder::get() LogWarning("Not finalized"); ThrowMsg(Exception::NotFinalized, "Not finalized"); } - BUF_MEM *bptr = 0; + BUF_MEM *bptr = nullptr; BIO_get_mem_ptr(m_b64, &bptr); - if (bptr == 0) { + if (!bptr) { LogError("Bio internal error"); ThrowMsg(Exception::InternalError, "Bio internal error"); } diff --git a/src/manager/crypto/sw-backend/internals.cpp b/src/manager/crypto/sw-backend/internals.cpp index 3875c2b..d5dfa23 100644 --- a/src/manager/crypto/sw-backend/internals.cpp +++ b/src/manager/crypto/sw-backend/internals.cpp @@ -293,7 +293,7 @@ InitCipherFn selectCipher(AlgoType type, size_t key_len = 32, bool encryption = RawBuffer asymmetricHelper(int (*cryptoFn)(int, const unsigned char*, unsigned char*, RSA*, int), - const std::string logPrefix, + const std::string &logPrefix, const EvpShPtr &pkey, const CryptoAlgorithm &alg, const RawBuffer &data) @@ -748,16 +748,13 @@ RawBuffer sign(EVP_PKEY *pkey, { validateParams(alg); - int rsa_padding = NOT_DEFINED; - const EVP_MD *md_algo = NULL; - HashAlgorithm hashTmp = HashAlgorithm::NONE; alg.getParam(ParamName::SV_HASH_ALGO, hashTmp); - md_algo = getMdAlgo(hashTmp); + const EVP_MD *md_algo = getMdAlgo(hashTmp); RSAPaddingAlgorithm rsaPad = RSAPaddingAlgorithm::NONE; alg.getParam(ParamName::SV_RSA_PADDING, rsaPad); - rsa_padding = getRsaPadding(rsaPad); + int rsa_padding = getRsaPadding(rsaPad); // // if((privateKey.getType() != KeyType::KEY_RSA_PRIVATE) && diff --git a/src/manager/service/ckm-service.cpp b/src/manager/service/ckm-service.cpp index 132e6a8..63095bb 100644 --- a/src/manager/service/ckm-service.cpp +++ b/src/manager/service/ckm-service.cpp @@ -170,7 +170,6 @@ RawBuffer CKMService::ProcessStorage(Credentials &cred, MessageBuffer &buffer) int tmpDataType = 0; Name name; Label label, accessorLabel; - std::string user; buffer.Deserialize(command); buffer.Deserialize(msgID); diff --git a/src/manager/service/ocsp.cpp b/src/manager/service/ocsp.cpp index 1dde8be..4172d0f 100644 --- a/src/manager/service/ocsp.cpp +++ b/src/manager/service/ocsp.cpp @@ -128,7 +128,7 @@ int OCSPModule::ocsp_verify(X509 *cert, X509 *issuer, STACK_OF(X509) *trustedCer ASN1_GENERALIZEDTIME *nextupd = NULL; int use_ssl = 0; int ocspStatus = -1; - int i = 0 ,tmpIdx = 0; + int i = 0; long nsec = MAX_VALIDITY_PERIOD, maxage = -1; char subj_buf[256]; int reason = 0; @@ -290,7 +290,7 @@ int OCSPModule::ocsp_verify(X509 *cert, X509 *issuer, STACK_OF(X509) *trustedCer if(trustedCerts != NULL) { trustedStore = X509_STORE_new(); - for(tmpIdx=0; tmpIdx Date: Wed, 14 Oct 2015 17:30:23 +0900 Subject: [PATCH 02/16] Version 0.1.17 Change-Id: I93d81a35d0bf4fccb4cafbd823014cdeb4939192 Signed-off-by: Kyungwook Tak --- packaging/key-manager.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/key-manager.spec b/packaging/key-manager.spec index 66ec3de..cc21d6f 100644 --- a/packaging/key-manager.spec +++ b/packaging/key-manager.spec @@ -1,6 +1,6 @@ Name: key-manager Summary: Central Key Manager and utilities -Version: 0.1.16 +Version: 0.1.17 Release: 1 Group: System/Security License: Apache-2.0 -- 2.7.4 From c66d4aa188580e7d4d2fdc096c0e128bc15ae85b Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Thu, 15 Oct 2015 13:56:56 +0900 Subject: [PATCH 03/16] Add gitignore file Change-Id: I491e88d2454a672b77e207aaf95d945c0b464591 Signed-off-by: Kyungwook Tak --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2e8b00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/cscope.files +/cscope.out +/tags -- 2.7.4 From f92539f4a8b231eea41b3f3e013a129ddbc4ed1f Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Thu, 15 Oct 2015 14:01:13 +0900 Subject: [PATCH 04/16] [ACR-429]Remove/Deprecated get cert chain with alias Change-Id: Ib1f775c98c41ef89d10199c03d17a69b7be46008 Signed-off-by: Kyungwook Tak --- src/include/ckmc/ckmc-manager.h | 54 ++------------------------------ src/manager/client-capi/ckmc-manager.cpp | 33 ------------------- 2 files changed, 2 insertions(+), 85 deletions(-) diff --git a/src/include/ckmc/ckmc-manager.h b/src/include/ckmc/ckmc-manager.h index 91cf458..d7ccd59 100644 --- a/src/include/ckmc/ckmc-manager.h +++ b/src/include/ckmc/ckmc-manager.h @@ -839,7 +839,6 @@ int ckmc_verify_signature(const char *public_key_alias, * * @pre User is already logged in and the user key is already loaded into memory in plain text form. * - * @see ckmc_get_cert_chain_with_alias()) * @see ckmc_cert_list_all_free() */ int ckmc_get_cert_chain(const ckmc_cert_s *cert, @@ -847,6 +846,7 @@ int ckmc_get_cert_chain(const ckmc_cert_s *cert, ckmc_cert_list_s **ppcert_chain_list); /** + * @deprecated Deprecated since 2.4. [Use ckmc_get_cert_chain() instead] * @brief Verifies a certificate chain using an alias list of untrusted certificates and return that * chain. * @@ -858,6 +858,7 @@ int ckmc_get_cert_chain(const ckmc_cert_s *cert, * storage. * @remarks You must destroy the newly created @a ppcert_chain_list by calling * ckmc_cert_list_all_free() if it is no longer needed. + * @remarks @a untrustedcerts shouldn't be protected with optional password. * * @param[in] cert The certificate to be verified * @param[in] untrustedcerts The alias list of untrusted CA certificates stored in key manager @@ -927,7 +928,6 @@ int ckmc_get_cert_chain_with_alias(const ckmc_cert_s *cert, * * @pre User is already logged in and the user key is already loaded into memory in plain text form. * - * @see ckmc_get_cert_chain_with_trustedcert_alias() * @see ckmc_cert_list_all_free() */ int ckmc_get_cert_chain_with_trustedcert(const ckmc_cert_s *cert, @@ -937,56 +937,6 @@ int ckmc_get_cert_chain_with_trustedcert(const ckmc_cert_s *cert, ckmc_cert_list_s **ppcert_chain_list); /** - * @brief Verifies a certificate chain and returns that chain using alias lists of untrusted and - * trusted certificates. - * - * @since_tizen 2.4 - * @remarks %http://tizen.org/privilege/keymanager (public level privilege) is no longer - * required to use this API since 3.0. - * - * @remarks If the alias list of trusted root certificates is provided as a user input, these - * certificates do not need to exist in the system's certificate storage. - * @remarks You must destroy the newly created @a ppcert_chain_list by calling - * ckmc_cert_list_all_free() if it is no longer needed. - * - * @param[in] cert The certificate to be verified - * @param[in] untrustedcerts The alias list of untrusted CA certificates stored in key - * manager to be used in verifying a certificate chain - * @param[in] trustedcerts The alias list of trusted CA certificates stored in key - * manager to be used in verifying a certificate chain - * @param[in] use_trustedsystemcerts The flag indicating the use of the trusted root certificates - * in the system's certificate storage - * @param[out] ppcert_chain_list The pointer to a newly created certificate chain's handle \n - * If an error occurs, @a *ppcert_chain_list will be null - * - * @return @c 0 on success and the signature is valid, - * otherwise a negative error value - * - * @retval #CKMC_ERROR_NONE Successful - * @retval #CKMC_ERROR_VERIFICATION_FAILED The certificate chain is not valid - * @retval #CKMC_ERROR_INVALID_PARAMETER Input parameter is invalid - * @retval #CKMC_ERROR_DB_LOCKED A user key is not loaded in memory (a user is not logged - * in) - * @retval #CKMC_ERROR_DB_ERROR Failed due to the error with unknown reason - * @retval #CKMC_ERROR_DB_ALIAS_UNKNOWN Alias does not exist - * @retval #CKMC_ERROR_INVALID_FORMAT The format of certificate is not valid - * @retval #CKMC_ERROR_PERMISSION_DENIED Failed to access key manager - * @retval #CKMC_ERROR_AUTHENTICATION_FAILED - * Some certificates were encrypted with password and could not - * be used - * - * @pre User is already logged in and the user key is already loaded into memory in plain text form. - * - * @see ckmc_get_cert_chain_with_trustedcert() - * @see ckmc_cert_list_all_free() - */ -int ckmc_get_cert_chain_with_trustedcert_alias(const ckmc_cert_s *cert, - const ckmc_alias_list_s *untrustedcerts, - const ckmc_alias_list_s *trustedcerts, - const bool use_trustedsystemcerts, - ckmc_cert_list_s **ppcert_chain_list); - -/** * @brief Perform OCSP which checks certificate is whether revoked or not. * * @since_tizen 2.4 diff --git a/src/manager/client-capi/ckmc-manager.cpp b/src/manager/client-capi/ckmc-manager.cpp index c998171..6565dc9 100644 --- a/src/manager/client-capi/ckmc-manager.cpp +++ b/src/manager/client-capi/ckmc-manager.cpp @@ -777,39 +777,6 @@ int ckmc_get_cert_chain_with_trustedcert(const ckmc_cert_s* cert, } KEY_MANAGER_CAPI -int ckmc_get_cert_chain_with_trustedcert_alias(const ckmc_cert_s* cert, - const ckmc_alias_list_s* untrustedcerts, - const ckmc_alias_list_s* trustedcerts, - const bool sys_certs, - ckmc_cert_list_s** ppcert_chain_list) -{ - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - CKM::CertificateShPtrVector ckm_cert_chain; - - if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || ppcert_chain_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } - - CKM::CertificateShPtr ckm_cert = _toCkmCertificate(cert); - if(ckm_cert.get() == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } - - CKM::AliasVector ckm_untrusted = _toCkmAliasVector(untrustedcerts); - CKM::AliasVector ckm_trusted = _toCkmAliasVector(trustedcerts); - - ret = mgr->getCertificateChain(ckm_cert, ckm_untrusted, ckm_trusted, sys_certs, ckm_cert_chain); - if( ret != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } - - *ppcert_chain_list = _toNewCkmCertList(ckm_cert_chain); - - return CKMC_ERROR_NONE; -} - -KEY_MANAGER_CAPI int ckmc_ocsp_check(const ckmc_cert_list_s *pcert_chain_list, ckmc_ocsp_status_e *ocsp_status) { if (pcert_chain_list == NULL -- 2.7.4 From daf30671621037680b113bbba8a3ce9d7ed50fbf Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Mon, 19 Oct 2015 11:43:03 +0900 Subject: [PATCH 05/16] Don't check handle value when allocation Change-Id: I3ab918652dc294107327bc3840bdd5c80bed0cc6 Signed-off-by: Kyungwook Tak --- src/manager/client-capi/ckmc-type.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manager/client-capi/ckmc-type.cpp b/src/manager/client-capi/ckmc-type.cpp index 7ce34be..a5de9f2 100644 --- a/src/manager/client-capi/ckmc-type.cpp +++ b/src/manager/client-capi/ckmc-type.cpp @@ -573,7 +573,7 @@ void ckmc_cert_list_all_free(ckmc_cert_list_s *first) KEY_MANAGER_CAPI int ckmc_param_list_new(ckmc_param_list_h *pparams) { - if (!pparams || *pparams) + if (!pparams) return CKMC_ERROR_INVALID_PARAMETER; *pparams = reinterpret_cast(new(std::nothrow)(CKM::CryptoAlgorithm)); @@ -650,7 +650,7 @@ void ckmc_param_list_free(ckmc_param_list_h params) KEY_MANAGER_CAPI int ckmc_generate_new_params(ckmc_algo_type_e type, ckmc_param_list_h *pparams) { - if (!pparams || *pparams) + if (!pparams) return CKMC_ERROR_INVALID_PARAMETER; ckmc_param_list_h params = NULL; -- 2.7.4 From d4853c5e67a3140776fb28626c30e8fe216d833c Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Mon, 19 Oct 2015 13:28:30 +0900 Subject: [PATCH 06/16] Version 0.1.18 Change-Id: I33e245d9b8b6b6ca81caa326d24e725c1821987b Signed-off-by: Kyungwook Tak --- packaging/key-manager.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/key-manager.spec b/packaging/key-manager.spec index cc21d6f..a545db1 100644 --- a/packaging/key-manager.spec +++ b/packaging/key-manager.spec @@ -1,6 +1,6 @@ Name: key-manager Summary: Central Key Manager and utilities -Version: 0.1.17 +Version: 0.1.18 Release: 1 Group: System/Security License: Apache-2.0 -- 2.7.4 From f04aff1fb8e32c1362795ca33940f695c3177769 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Mon, 14 Sep 2015 15:05:44 +0200 Subject: [PATCH 07/16] Add support for binary data to GStore [Problem] Binary data can be imported into store but can't be retrieved from it. [Solution] Introduce another intermediate class in GKey hierarhy to support binary data. [Verification] Run tests Change-Id: I45bf5d0a81188f13b0925e982243fdf37b569529 --- src/CMakeLists.txt | 4 +-- src/manager/crypto/generic-backend/exception.h | 2 +- .../crypto/generic-backend/{gkey.h => gobj.h} | 12 ++++---- src/manager/crypto/generic-backend/gstore.h | 4 +-- src/manager/crypto/sw-backend/internals.h | 2 +- src/manager/crypto/sw-backend/{key.cpp => obj.cpp} | 20 ++++++-------- src/manager/crypto/sw-backend/{key.h => obj.h} | 32 ++++++++++++---------- src/manager/crypto/sw-backend/store.cpp | 10 +++++-- src/manager/crypto/sw-backend/store.h | 4 +-- src/manager/crypto/tz-backend/{key.cpp => obj.cpp} | 2 +- src/manager/crypto/tz-backend/{key.h => obj.h} | 8 +++--- src/manager/crypto/tz-backend/store.cpp | 4 +-- src/manager/crypto/tz-backend/store.h | 4 +-- src/manager/main/service-messages.h | 6 ++-- src/manager/service/ckm-logic.cpp | 8 +++--- src/manager/service/ckm-logic.h | 4 +-- src/manager/service/ckm-service.cpp | 2 +- src/manager/service/iencryption-service.h | 2 +- tools/ckm_db_tool/CMakeLists.txt | 2 +- 19 files changed, 67 insertions(+), 65 deletions(-) rename src/manager/crypto/generic-backend/{gkey.h => gobj.h} (91%) rename src/manager/crypto/sw-backend/{key.cpp => obj.cpp} (92%) rename src/manager/crypto/sw-backend/{key.h => obj.h} (82%) rename src/manager/crypto/tz-backend/{key.cpp => obj.cpp} (97%) rename src/manager/crypto/tz-backend/{key.h => obj.h} (89%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index affdc96..131e6d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -66,11 +66,11 @@ SET(KEY_MANAGER_SOURCES ${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 - ${KEY_MANAGER_PATH}/crypto/sw-backend/key.cpp + ${KEY_MANAGER_PATH}/crypto/sw-backend/obj.cpp ${KEY_MANAGER_PATH}/crypto/sw-backend/internals.cpp ${KEY_MANAGER_PATH}/crypto/sw-backend/store.cpp ${KEY_MANAGER_PATH}/crypto/platform/decider.cpp - ${KEY_MANAGER_PATH}/crypto/tz-backend/key.cpp + ${KEY_MANAGER_PATH}/crypto/tz-backend/obj.cpp ${KEY_MANAGER_PATH}/crypto/tz-backend/store.cpp ${SECURITY_MANAGER_WRAPPER_PATH} ${CYNARA_WRAPPER_PATH} diff --git a/src/manager/crypto/generic-backend/exception.h b/src/manager/crypto/generic-backend/exception.h index 18e5b70..cdd4415 100644 --- a/src/manager/crypto/generic-backend/exception.h +++ b/src/manager/crypto/generic-backend/exception.h @@ -28,7 +28,7 @@ namespace Crypto { typedef CKM::Exc::InputParam InputParam; typedef CKM::Exc::InternalError InternalError; -typedef CKM::Exc::InternalError KeyNotSupported; +typedef CKM::Exc::InternalError DataTypeNotSupported; typedef CKM::Exc::InternalError OperationNotSupported; typedef CKM::Exc::InternalError WrongBackend; diff --git a/src/manager/crypto/generic-backend/gkey.h b/src/manager/crypto/generic-backend/gobj.h similarity index 91% rename from src/manager/crypto/generic-backend/gkey.h rename to src/manager/crypto/generic-backend/gobj.h index b06926d..0857a0e 100644 --- a/src/manager/crypto/generic-backend/gkey.h +++ b/src/manager/crypto/generic-backend/gobj.h @@ -14,7 +14,7 @@ * limitations under the License */ /* - * @file gkey.h + * @file gobj.h * @author Bartłomiej Grzelewski (b.grzelewski@samsung.com) * @version 1.0 */ @@ -29,9 +29,9 @@ namespace CKM { namespace Crypto { -class GKey { +class GObj { protected: - GKey(){} + GObj(){} public: virtual RawBuffer getBinary() const { ThrowErr(Exc::Crypto::OperationNotSupported); @@ -53,11 +53,11 @@ public: ThrowErr(Exc::Crypto::OperationNotSupported); } - virtual ~GKey () {} + virtual ~GObj () {} }; -typedef std::unique_ptr GKeyUPtr; -typedef std::shared_ptr GKeyShPtr; +typedef std::unique_ptr GObjUPtr; +typedef std::shared_ptr GObjShPtr; } // namespace Crypto } // namespace CKM diff --git a/src/manager/crypto/generic-backend/gstore.h b/src/manager/crypto/generic-backend/gstore.h index 2c8aca7..30e177d 100644 --- a/src/manager/crypto/generic-backend/gstore.h +++ b/src/manager/crypto/generic-backend/gstore.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ namespace Crypto { class GStore { public: - virtual GKeyUPtr getKey(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported); } + virtual GObjUPtr getObject(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported); } virtual TokenPair generateAKey(const CryptoAlgorithm &) { ThrowErr(Exc::Crypto::OperationNotSupported); } virtual Token generateSKey(const CryptoAlgorithm &) { ThrowErr(Exc::Crypto::OperationNotSupported); } virtual Token import(DataType, const RawBuffer &) { ThrowErr(Exc::Crypto::OperationNotSupported); } diff --git a/src/manager/crypto/sw-backend/internals.h b/src/manager/crypto/sw-backend/internals.h index c71d106..3b54394 100644 --- a/src/manager/crypto/sw-backend/internals.h +++ b/src/manager/crypto/sw-backend/internals.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include #define EVP_SUCCESS 1 // DO NOTCHANGE THIS VALUE #define EVP_FAIL 0 // DO NOTCHANGE THIS VALUE diff --git a/src/manager/crypto/sw-backend/key.cpp b/src/manager/crypto/sw-backend/obj.cpp similarity index 92% rename from src/manager/crypto/sw-backend/key.cpp rename to src/manager/crypto/sw-backend/obj.cpp index b1e57ff..0b602a3 100644 --- a/src/manager/crypto/sw-backend/key.cpp +++ b/src/manager/crypto/sw-backend/obj.cpp @@ -14,7 +14,7 @@ * limitations under the License */ /* - * @file key.cpp + * @file obj.cpp * @author Bartłomiej Grzelewski (b.grzelewski@samsung.com) * @version 1.0 */ @@ -27,7 +27,7 @@ #include #include -#include +#include #include #define EVP_SUCCESS 1 // DO NOTCHANGE THIS VALUE @@ -59,8 +59,8 @@ AlgoType key2algo(DataType type) { typedef std::unique_ptr> BioUniquePtr; -RawBuffer SKey::getBinary() const { - return m_key; +RawBuffer BData::getBinary() const { + return m_raw; } RawBuffer SKey::encrypt(const CryptoAlgorithm &alg, const RawBuffer &data) @@ -81,10 +81,6 @@ RawBuffer AKey::sign( return Internals::sign(getEvpShPtr().get(), algWithType, message); } -RawBuffer AKey::getBinary() const { - return m_key; -} - int AKey::verify(const CryptoAlgorithm &alg, const RawBuffer &message, const RawBuffer &sign) { CryptoAlgorithm algWithType(alg); EVP_PKEY* evp = getEvpShPtr().get(); @@ -129,14 +125,14 @@ EvpShPtr AKey::getEvpShPtr() { if (!pkey) { (void)BIO_reset(bio.get()); - BIO_write(bio.get(), m_key.data(), m_key.size()); + BIO_write(bio.get(), m_raw.data(), m_raw.size()); pkey = d2i_PrivateKey_bio(bio.get(), NULL); LogDebug("Trying d2i_PrivateKey_bio Status: " << (void*)pkey); } if (!pkey) { (void)BIO_reset(bio.get()); - BIO_write(bio.get(), m_key.data(), m_key.size()); + BIO_write(bio.get(), m_raw.data(), m_raw.size()); pkey = d2i_PUBKEY_bio(bio.get(), NULL); LogDebug("Trying d2i_PUBKEY_bio Status: " << (void*)pkey); } @@ -153,8 +149,8 @@ EvpShPtr Cert::getEvpShPtr() { if (m_evp) return m_evp; - int size = static_cast(m_key.size()); - const unsigned char *ptr = reinterpret_cast(m_key.data()); + int size = static_cast(m_raw.size()); + const unsigned char *ptr = reinterpret_cast(m_raw.data()); X509 *x509 = d2i_X509(NULL, &ptr, size); diff --git a/src/manager/crypto/sw-backend/key.h b/src/manager/crypto/sw-backend/obj.h similarity index 82% rename from src/manager/crypto/sw-backend/key.h rename to src/manager/crypto/sw-backend/obj.h index d5b7bfe..c56354f 100644 --- a/src/manager/crypto/sw-backend/key.h +++ b/src/manager/crypto/sw-backend/obj.h @@ -14,7 +14,7 @@ * limitations under the License */ /* - * @file key.h + * @file obj.h * @author Bartłomiej Grzelewski (b.grzelewski@samsung.com) * @version 1.0 */ @@ -23,7 +23,7 @@ #include -#include +#include #include namespace CKM { @@ -33,39 +33,41 @@ namespace SW { typedef std::unique_ptr> ContextUPtr; typedef std::shared_ptr EvpShPtr; -class SKey : public GKey { +class BData : public GObj { public: - SKey(RawBuffer buffer, DataType keyType) - : m_key(std::move(buffer)) + BData(RawBuffer buffer, DataType keyType) + : m_raw(std::move(buffer)) , m_type(keyType) {} virtual RawBuffer getBinary() const; - virtual RawBuffer encrypt(const CryptoAlgorithm &, const RawBuffer &); - virtual RawBuffer decrypt(const CryptoAlgorithm &, const RawBuffer &); protected: - RawBuffer m_key; + RawBuffer m_raw; DataType m_type; }; -class AKey : public GKey { +class SKey : public BData { public: - AKey(RawBuffer buffer, DataType dataType) - : m_key(std::move(buffer)) - , m_type(dataType) + SKey(RawBuffer buffer, DataType keyType) : BData(std::move(buffer), keyType) + {} + + virtual RawBuffer encrypt(const CryptoAlgorithm &, const RawBuffer &); + virtual RawBuffer decrypt(const CryptoAlgorithm &, const RawBuffer &); +}; + +class AKey : public BData { +public: + AKey(RawBuffer buffer, DataType dataType) : BData(std::move(buffer), dataType) {} virtual RawBuffer sign(const CryptoAlgorithm &alg, const RawBuffer &message); virtual int verify(const CryptoAlgorithm &alg, const RawBuffer &message, const RawBuffer &sign); virtual RawBuffer encrypt(const CryptoAlgorithm &, const RawBuffer &); virtual RawBuffer decrypt(const CryptoAlgorithm &, const RawBuffer &); - virtual RawBuffer getBinary() const; virtual ~AKey(){} protected: virtual EvpShPtr getEvpShPtr(); EvpShPtr m_evp; - RawBuffer m_key; - DataType m_type; }; class Cert : public AKey { diff --git a/src/manager/crypto/sw-backend/store.cpp b/src/manager/crypto/sw-backend/store.cpp index 77bcb7a..0c6669a 100644 --- a/src/manager/crypto/sw-backend/store.cpp +++ b/src/manager/crypto/sw-backend/store.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -43,7 +43,7 @@ Store::Store(CryptoBackend backendId) { } -GKeyUPtr Store::getKey(const Token &token) { +GObjUPtr Store::getObject(const Token &token) { if (token.backendId != m_backendId) { ThrowErr(Exc::Crypto::WrongBackend, "Decider choose wrong backend!"); } @@ -60,7 +60,11 @@ GKeyUPtr Store::getKey(const Token &token) { return make_unique(token.data, token.dataType); } - ThrowErr(Exc::Crypto::KeyNotSupported, + if (token.dataType.isBinaryData()) { + return make_unique(token.data, token.dataType); + } + + ThrowErr(Exc::Crypto::DataTypeNotSupported, "This type of data is not supported by openssl backend: ", (int)token.dataType); } diff --git a/src/manager/crypto/sw-backend/store.h b/src/manager/crypto/sw-backend/store.h index cb23155..552f5bc 100644 --- a/src/manager/crypto/sw-backend/store.h +++ b/src/manager/crypto/sw-backend/store.h @@ -20,7 +20,7 @@ */ #pragma once -#include +#include #include namespace CKM { @@ -31,7 +31,7 @@ class Store : public GStore { public: explicit Store(CryptoBackend backendId); - virtual GKeyUPtr getKey(const Token &token); + virtual GObjUPtr getObject(const Token &token); virtual TokenPair generateAKey(const CryptoAlgorithm &); virtual Token generateSKey(const CryptoAlgorithm &); virtual Token import(DataType dataType, const RawBuffer &buffer); diff --git a/src/manager/crypto/tz-backend/key.cpp b/src/manager/crypto/tz-backend/obj.cpp similarity index 97% rename from src/manager/crypto/tz-backend/key.cpp rename to src/manager/crypto/tz-backend/obj.cpp index 1ebeec6..b1109b7 100644 --- a/src/manager/crypto/tz-backend/key.cpp +++ b/src/manager/crypto/tz-backend/obj.cpp @@ -14,7 +14,7 @@ * limitations under the License */ /* - * @file key.cpp + * @file obj.cpp * @author Bartłomiej Grzelewski (b.grzelewski@samsung.com) * @version 1.0 */ diff --git a/src/manager/crypto/tz-backend/key.h b/src/manager/crypto/tz-backend/obj.h similarity index 89% rename from src/manager/crypto/tz-backend/key.h rename to src/manager/crypto/tz-backend/obj.h index c911cf8..60e3a61 100644 --- a/src/manager/crypto/tz-backend/key.h +++ b/src/manager/crypto/tz-backend/obj.h @@ -14,26 +14,26 @@ * limitations under the License */ /* - * @file key.h + * @file obj.h * @author Bartłomiej Grzelewski (b.grzelewski@samsung.com) * @version 1.0 */ #pragma once -#include +#include namespace CKM { namespace Crypto { namespace TZ { -class SKey : public GKey { +class SKey : public GObj { public: SKey(){} virtual ~SKey(){} protected: }; -class AKey : public GKey { +class AKey : public GObj { public: AKey(){} virtual ~AKey(){} diff --git a/src/manager/crypto/tz-backend/store.cpp b/src/manager/crypto/tz-backend/store.cpp index 11ac0b0..fe3fae7 100644 --- a/src/manager/crypto/tz-backend/store.cpp +++ b/src/manager/crypto/tz-backend/store.cpp @@ -19,7 +19,7 @@ * @version 1.0 */ #include -#include +#include #include namespace CKM { @@ -30,7 +30,7 @@ Store::Store(CryptoBackend backendId) : GStore(backendId) {} -GKeyUPtr Store::getKey(const Token &) { +GObjUPtr Store::getObject(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!"); } diff --git a/src/manager/crypto/tz-backend/store.h b/src/manager/crypto/tz-backend/store.h index ee5d24b..be3595c 100644 --- a/src/manager/crypto/tz-backend/store.h +++ b/src/manager/crypto/tz-backend/store.h @@ -20,7 +20,7 @@ */ #pragma once -#include +#include #include namespace CKM { @@ -31,7 +31,7 @@ class Store : public GStore { public: explicit Store(CryptoBackend backendId); - virtual GKeyUPtr getKey(const Token &token); + virtual GObjUPtr getObject(const Token &token); virtual TokenPair generateAKey(const CryptoAlgorithm &); virtual Token import(DataType dataType, const RawBuffer &buffer); virtual void destroy(const Token &){} diff --git a/src/manager/main/service-messages.h b/src/manager/main/service-messages.h index f23711f..26989ee 100644 --- a/src/manager/main/service-messages.h +++ b/src/manager/main/service-messages.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include namespace CKM { @@ -64,12 +64,12 @@ struct MsgKeyRequest : public MsgBase // key response struct MsgKeyResponse : public MsgBase { - MsgKeyResponse(int id, const Crypto::GKeyShPtr& key, int errorCode = CKM_API_SUCCESS) : + MsgKeyResponse(int id, const Crypto::GObjShPtr& key, int errorCode = CKM_API_SUCCESS) : MsgBase(id), key(key), error(errorCode) {} - Crypto::GKeyShPtr key; + Crypto::GObjShPtr key; int error; }; diff --git a/src/manager/service/ckm-logic.cpp b/src/manager/service/ckm-logic.cpp index 217d0cd..728bfc5 100644 --- a/src/manager/service/ckm-logic.cpp +++ b/src/manager/service/ckm-logic.cpp @@ -510,14 +510,14 @@ int CKMLogic::getKeyForService( const Name &name, const Label &label, const Password &pass, - Crypto::GKeyShPtr &key) + Crypto::GObjShPtr &key) { DB::Row row; try { // Key is for internal service use. It won't be exported to the client int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, label, pass, row); if (retCode == CKM_API_SUCCESS) - key = m_decider.getStore(row).getKey(row); + key = m_decider.getStore(row).getObject(row); return retCode; } catch (const DB::Crypto::Exception::Base &e) { LogError("DB::Crypto failed with message: " << e.GetMessage()); @@ -1492,7 +1492,7 @@ RawBuffer CKMLogic::createSignature( try { retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, privateKeyName, ownerLabel, password, row); if(retCode == CKM_API_SUCCESS) { - signature = m_decider.getStore(row).getKey(row)->sign(cryptoAlg, message); + signature = m_decider.getStore(row).getObject(row)->sign(cryptoAlg, message); } } catch (const DB::Crypto::Exception::Base &e) { LogError("DB::Crypto failed with message: " << e.GetMessage()); @@ -1540,7 +1540,7 @@ RawBuffer CKMLogic::verifySignature( } if (retCode == CKM_API_SUCCESS) { - retCode = m_decider.getStore(row).getKey(row)->verify(params, message, signature); + retCode = m_decider.getStore(row).getObject(row)->verify(params, message, signature); } } catch (const Exc::Exception &e) { retCode = e.error(); diff --git a/src/manager/service/ckm-logic.h b/src/manager/service/ckm-logic.h index 336b346..c07225b 100644 --- a/src/manager/service/ckm-logic.h +++ b/src/manager/service/ckm-logic.h @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include @@ -206,7 +206,7 @@ public: const Name &name, const Label &label, const Password& pass, - Crypto::GKeyShPtr& key); + Crypto::GObjShPtr& key); protected: int unlockSystemDB(); diff --git a/src/manager/service/ckm-service.cpp b/src/manager/service/ckm-service.cpp index 63095bb..6a744bd 100644 --- a/src/manager/service/ckm-service.cpp +++ b/src/manager/service/ckm-service.cpp @@ -396,7 +396,7 @@ RawBuffer CKMService::ProcessStorage(Credentials &cred, MessageBuffer &buffer) void CKMService::ProcessMessage(MsgKeyRequest msg) { - Crypto::GKeyShPtr key; + Crypto::GObjShPtr key; int ret = m_logic->getKeyForService(msg.cred, msg.name, msg.label, diff --git a/src/manager/service/iencryption-service.h b/src/manager/service/iencryption-service.h index 2c1d906..d69800a 100644 --- a/src/manager/service/iencryption-service.h +++ b/src/manager/service/iencryption-service.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/tools/ckm_db_tool/CMakeLists.txt b/tools/ckm_db_tool/CMakeLists.txt index a2fd9db..c8fb53c 100644 --- a/tools/ckm_db_tool/CMakeLists.txt +++ b/tools/ckm_db_tool/CMakeLists.txt @@ -64,7 +64,7 @@ SET(CKM_DB_TOOL_SOURCES ${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 - ${KEY_MANAGER_PATH}/crypto/sw-backend/key.cpp + ${KEY_MANAGER_PATH}/crypto/sw-backend/obj.cpp ${KEY_MANAGER_PATH}/crypto/sw-backend/internals.cpp ${KEY_MANAGER_PATH}/crypto/sw-backend/store.cpp ${KEY_MANAGER_PATH}/crypto/platform/decider.cpp -- 2.7.4 From b2d277acd0398562a7e9968c1d6c59517178c0f8 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Thu, 24 Sep 2015 09:07:55 +0200 Subject: [PATCH 08/16] Add scheme encryption test db generator [Problem] A database filled with all kind of data is needed for encryption scheme tests. [Solution] Add tool that fills the database with different kinds of data. [Verification] Run ckm-db-generator. Use ckm_db_tool 7654 db-pass to verify that all types of data is present in db. Change-Id: If2d912afdfe96a535df98c5a6c03a2acb1c84af5 --- packaging/key-manager.spec | 8 + tests/CMakeLists.txt | 4 + tests/encryption-scheme/CMakeLists.txt | 63 +++++ tests/encryption-scheme/db/db-7654 | Bin 0 -> 75776 bytes tests/encryption-scheme/db/db-key-7654 | Bin 0 -> 116 bytes tests/encryption-scheme/db/key-7654 | Bin 0 -> 116 bytes tests/encryption-scheme/encryption-scheme.p12 | Bin 0 -> 4373 bytes tests/encryption-scheme/generate-db.cpp | 38 +++ tests/encryption-scheme/scheme-test.cpp | 368 ++++++++++++++++++++++++++ tests/encryption-scheme/scheme-test.h | 62 +++++ tests/encryption-scheme/smack-access.cpp | 52 ++++ tests/encryption-scheme/smack-access.h | 42 +++ 12 files changed, 637 insertions(+) create mode 100644 tests/encryption-scheme/CMakeLists.txt create mode 100644 tests/encryption-scheme/db/db-7654 create mode 100644 tests/encryption-scheme/db/db-key-7654 create mode 100644 tests/encryption-scheme/db/key-7654 create mode 100644 tests/encryption-scheme/encryption-scheme.p12 create mode 100644 tests/encryption-scheme/generate-db.cpp create mode 100644 tests/encryption-scheme/scheme-test.cpp create mode 100644 tests/encryption-scheme/scheme-test.h create mode 100644 tests/encryption-scheme/smack-access.cpp create mode 100644 tests/encryption-scheme/smack-access.h diff --git a/packaging/key-manager.spec b/packaging/key-manager.spec index a545db1..a054ff6 100644 --- a/packaging/key-manager.spec +++ b/packaging/key-manager.spec @@ -151,6 +151,9 @@ cp tests/XML_1_okay.xsd %{buildroot}/usr/share/ckm-db-test/ cp tests/XML_1_wrong.xml %{buildroot}/usr/share/ckm-db-test/ cp tests/XML_1_wrong.xsd %{buildroot}/usr/share/ckm-db-test/ cp tests/XML_2_structure.xml %{buildroot}/usr/share/ckm-db-test/ +cp tests/encryption-scheme/db/db-7654 %{buildroot}/usr/share/ckm-db-test/db-7654 +cp tests/encryption-scheme/db/db-key-7654 %{buildroot}/usr/share/ckm-db-test/db-key-7654 +cp tests/encryption-scheme/db/key-7654 %{buildroot}/usr/share/ckm-db-test/key-7654 %make_install %install_service multi-user.target.wants central-key-manager.service @@ -289,5 +292,10 @@ fi %{_datadir}/ckm-db-test/XML_1_wrong.xml %{_datadir}/ckm-db-test/XML_1_wrong.xsd %{_datadir}/ckm-db-test/XML_2_structure.xml +%{_datadir}/ckm-db-test/db-7654 +%{_datadir}/ckm-db-test/db-key-7654 +%{_datadir}/ckm-db-test/key-7654 +%{_datadir}/ckm-db-test/encryption-scheme.p12 %{_bindir}/ckm_so_loader %{_bindir}/ckm_db_tool +%{_bindir}/ckm_generate_db diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 650c5e7..62fea85 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,6 +9,8 @@ ADD_DEFINITIONS( "-DBOOST_TEST_DYN_LINK" ) SET(KEY_MANAGER_SRC_PATH ${PROJECT_SOURCE_DIR}/src) SET(KEY_MANAGER_PATH ${PROJECT_SOURCE_DIR}/src/manager) +SET(TARGET_ENCRYPTION_SCHEME_COMMON encryption-scheme-common) + ################################################################################ @@ -65,3 +67,5 @@ TARGET_LINK_LIBRARIES(${TARGET_TEST_MERGED} ) INSTALL(TARGETS ${TARGET_TEST_MERGED} DESTINATION bin) + +ADD_SUBDIRECTORY(encryption-scheme) \ No newline at end of file diff --git a/tests/encryption-scheme/CMakeLists.txt b/tests/encryption-scheme/CMakeLists.txt new file mode 100644 index 0000000..8503859 --- /dev/null +++ b/tests/encryption-scheme/CMakeLists.txt @@ -0,0 +1,63 @@ +# 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 CMakeLists.txt +# @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) +# @brief +# + +INCLUDE(FindPkgConfig) + +# common encryption scheme library +PKG_CHECK_MODULES(ENCRYPTION_SCHEME_DEP + libsmack + REQUIRED) + +SET(ENCRYPTION_SCHEME_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/smack-access.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/scheme-test.cpp +) + +INCLUDE_DIRECTORIES(SYSTEM ${ENCRYPTION_SCHEME_DEP_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ) + +ADD_LIBRARY(${TARGET_ENCRYPTION_SCHEME_COMMON} STATIC ${ENCRYPTION_SCHEME_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_ENCRYPTION_SCHEME_COMMON} + ${ENCRYPTION_SCHEME_DEP_LIBRARIES} + ${TARGET_KEY_MANAGER_CLIENT} + ${TARGET_KEY_MANAGER_CONTROL_CLIENT} +) + +INSTALL(TARGETS ${TARGET_ENCRYPTION_SCHEME_COMMON} DESTINATION ${LIB_INSTALL_DIR}) + + + +# binary for filling db +SET(TARGET_CKM_GENERATOR "ckm_generate_db") + +SET(GENERATOR_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/generate-db.cpp +) + +ADD_EXECUTABLE(${TARGET_CKM_GENERATOR} ${GENERATOR_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_CKM_GENERATOR} + ${TARGET_ENCRYPTION_SCHEME_COMMON} +) + +INSTALL(TARGETS ${TARGET_CKM_GENERATOR} DESTINATION bin) + + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/encryption-scheme.p12 DESTINATION /usr/share/ckm-db-test) \ No newline at end of file diff --git a/tests/encryption-scheme/db/db-7654 b/tests/encryption-scheme/db/db-7654 new file mode 100644 index 0000000000000000000000000000000000000000..4ed797a1a8a151344a63d2d2b89ef11ddcc6dd5d GIT binary patch literal 75776 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 z≫KwtowcyIlvsN^q8U^=Q*n$X?Vl*RtU;y25TIS_oh7&PnVAw1i> zw2({L`ttSTN6b}J)#${9Dxd6cT5Q|K$ikOJiju=>&vv`rfn^S$s%!|OeFlz*bDlGB zpf}uY$v);*@g4T+0JJAJ^}SxR!z`V0X>s!SqrUUYwTWAx&h^gPnpIiu9O7{K0s#T` z$#;;f4m^pCM(_Jj7Ad1FkkxlH7l9(1?WOOGG+^O?aTOL)U@UkiVLP5B2f4=>jlXs7srOd*_J6v7Ise{fA0qX?uE)O*JJ@mv+$xTH`6{6pU7H#< zL{?@A$me@4P_&cP-d*?Al|?vHc5-YQuIo|lUcTk=Mh*Q1t3E^>kR`15tz|C+0=p)N zO8^~y?$_`Q?M|Cf(8DXil_P$62$oN%WO5_M+z7_|NEw?b;?~pn4}>DM?0rX2M84Y| z#qN?b*6;@(TAo7*i_?_UT@9mr2j-#nW1viYkgbfNVJOXo4>CR7r@wl17N1&3Q{%|; zB)=%L;+l>6i*b?4PgG`P)puCh99L?zY2H-~gXNImeDK z*)x)Pn?3@m-EbYkx+?`xoT;XRcKA9_^+Y!(+{UOj)0YkMH7zK~?YxM9@W)fxvdHp| zaJeWq$yB_3fLq>Y5KSl9cp@OBBZ_~|H#q!&ep_{$7O`Gp09nEOxfPr?tnWgHBlXf7D;hWUsBy)oNbCdzK;WF q$1?oc(2TXoe!AL_pil7WGLdI-4=HwPlMLcIP|i?OU&oALL|~;YD|2%I literal 0 HcmV?d00001 diff --git a/tests/encryption-scheme/db/db-key-7654 b/tests/encryption-scheme/db/db-key-7654 new file mode 100644 index 0000000000000000000000000000000000000000..1273a0d88662584983b8ca6b8957f6bac98e052f GIT binary patch literal 116 zcmY#jU|=veGc^HHIKjsR`vta>EU#HjE=|ZNnzS^;@Wj*)pSU9o4ZAB-_ literal 0 HcmV?d00001 diff --git a/tests/encryption-scheme/db/key-7654 b/tests/encryption-scheme/db/key-7654 new file mode 100644 index 0000000000000000000000000000000000000000..5f5ef27b98fc9c15630e34ee70b3c859a93a8110 GIT binary patch literal 116 zcmY#jU|=veGc^HHIDv2DD~rbhnVa^6ZQ{>3WaQVOZ+8BRtFoV2$jh+WgTW>zWZE|# xUAbK+{b~QLq}U4M$Js$$vij#$IZ{{NW~?x>Oz^p!TJXG{dxBDcuA#N!b^uJ-BxwKu literal 0 HcmV?d00001 diff --git a/tests/encryption-scheme/encryption-scheme.p12 b/tests/encryption-scheme/encryption-scheme.p12 new file mode 100644 index 0000000000000000000000000000000000000000..18b2220f9fc257a7cee7ce4e89f51d38ee4cef85 GIT binary patch literal 4373 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 +#include + +#include + +int main() +{ + try { + SchemeTest st; + st.FillDb(); + return 0; + } catch (const std::runtime_error& e) { + std::cerr << e.what() << std::endl; + return -1; + } +} + diff --git a/tests/encryption-scheme/scheme-test.cpp b/tests/encryption-scheme/scheme-test.cpp new file mode 100644 index 0000000..5422bd2 --- /dev/null +++ b/tests/encryption-scheme/scheme-test.cpp @@ -0,0 +1,368 @@ +/* + * 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 scheme-test.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace CKM; +using namespace std; + +namespace { +const uid_t UID = 7654; +const gid_t GID = 7654; +const char* const DBPASS = "db-pass"; +const char* const LABEL = "my-label"; +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"; + +enum { + NO_PASS = 0, + PASS = 1 +}; + +enum { + NO_EXP = 0, + EXP = 1 +}; + +// [password][exportable] +Policy policy[2][2] = { + {{ Password(), false }, { Password(), true }}, + {{ TEST_PASS, false }, { TEST_PASS, true }}, +}; + +struct Group { + enum { + KEY_PAIR, + CERT_CHAIN, + SINGLE_ITEM + } type; + Items items; +}; + +Group GROUPS[] = { + // Data + { Group::SINGLE_ITEM, { + Item("data-alias1", DataType::BINARY_DATA, policy[NO_PASS][EXP]) + }}, + { Group::SINGLE_ITEM, { + Item("data-alias2", DataType::BINARY_DATA, policy[PASS][EXP]) + }}, + + // RSA keys + { Group::KEY_PAIR, { + 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, { + 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, { + 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, { + 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, { + 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]), + }}, + + // AES + { Group::SINGLE_ITEM, { + Item("key-aes-alias1", DataType::KEY_AES, policy[NO_PASS][NO_EXP]), + }}, + { Group::SINGLE_ITEM, { + Item("key-aes-alias2", DataType::KEY_AES, policy[NO_PASS][EXP]), + }}, + { Group::SINGLE_ITEM, { + Item("key-aes-alias3", DataType::KEY_AES, policy[PASS][NO_EXP]), + }}, + { Group::SINGLE_ITEM, { + Item("key-aes-alias4", DataType::KEY_AES, policy[PASS][EXP]), + }}, + + // Certificates + { Group::CERT_CHAIN, { + Item("cert-root-alias1", DataType::CERTIFICATE, policy[NO_PASS][NO_EXP]), + Item("cert-im-ca-alias1", DataType::CERTIFICATE, policy[NO_PASS][NO_EXP]), + Item("cert-leaf-alias1", DataType::CERTIFICATE, policy[NO_PASS][NO_EXP]), + }}, + { Group::CERT_CHAIN, { + Item("cert-root-alias2", DataType::CERTIFICATE, policy[NO_PASS][EXP]), + Item("cert-im-ca-alias2", DataType::CERTIFICATE, policy[NO_PASS][EXP]), + Item("cert-leaf-alias2", DataType::CERTIFICATE, policy[NO_PASS][EXP]), + }}, + { Group::CERT_CHAIN, { + Item("cert-root-alias3", DataType::CERTIFICATE, policy[PASS][NO_EXP]), + Item("cert-im-ca-alias3", DataType::CERTIFICATE, policy[PASS][NO_EXP]), + Item("cert-leaf-alias3", DataType::CERTIFICATE, policy[PASS][NO_EXP]), + }}, + { Group::CERT_CHAIN, { + Item("cert-root-alias4", DataType::CERTIFICATE, policy[PASS][EXP]), + Item("cert-im-ca-alias4", DataType::CERTIFICATE, policy[PASS][EXP]), + Item("cert-leaf-alias4", DataType::CERTIFICATE, policy[PASS][EXP]), + }}, + + // PKCS + { Group::SINGLE_ITEM, { + Item("pkcs-alias1", DataType::CHAIN_CERT_0, policy[NO_PASS][NO_EXP]), + }}, + { Group::SINGLE_ITEM, { + Item("pkcs-alias2", DataType::CHAIN_CERT_0, policy[NO_PASS][EXP]), + }}, + { Group::SINGLE_ITEM, { + Item("pkcs-alias3", DataType::CHAIN_CERT_0, policy[PASS][NO_EXP]), + }}, + { Group::SINGLE_ITEM, { + Item("pkcs-alias4", DataType::CHAIN_CERT_0, policy[PASS][EXP]), + }}, +}; + +const size_t CHAIN_SIZE = 3; + +// TEST_ROOT_CA, expires 2035 +std::string TEST_ROOT_CA = + "-----BEGIN CERTIFICATE-----\n" + "MIIDnzCCAoegAwIBAgIJAMH/ADkC5YSTMA0GCSqGSIb3DQEBBQUAMGYxCzAJBgNV\n" + "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ0wCwYDVQQKDARBQ01FMRAwDgYD\n" + "VQQLDAdUZXN0aW5nMSEwHwYDVQQDDBhUZXN0IHJvb3QgY2EgY2VydGlmaWNhdGUw\n" + "HhcNMTQxMjMwMTcyMTUyWhcNMjQxMjI3MTcyMTUyWjBmMQswCQYDVQQGEwJBVTET\n" + "MBEGA1UECAwKU29tZS1TdGF0ZTENMAsGA1UECgwEQUNNRTEQMA4GA1UECwwHVGVz\n" + "dGluZzEhMB8GA1UEAwwYVGVzdCByb290IGNhIGNlcnRpZmljYXRlMIIBIjANBgkq\n" + "hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0EJRdUtd2th0vTVF7QxvDKzyFCF3w9vC\n" + "9IDE/Yr12w+a9jd0s7/eG96qTHIYffS3B7x2MB+d4n+SR3W0qmYh7xk8qfEgH3da\n" + "eDoV59IZ9r543KM+g8jm6KffYGX1bIJVVY5OhBRbO9nY6byYpd5kbCIUB6dCf7/W\n" + "rQl1aIdLGFIegAzPGFPXDcU6F192686x54bxt/itMX4agHJ9ZC/rrTBIZghVsjJo\n" + "5/AH5WZpasv8sfrGiiohAxtieoYoJkv5MOYP4/2lPlOY+Cgw1Yoz+HHv31AllgFs\n" + "BquBb/kJVmCCNsAOcnvQzTZUsW/TXz9G2nwRdqI1nSy2JvVjZGsqGQIDAQABo1Aw\n" + "TjAdBgNVHQ4EFgQUt6pkzFt1PZlfYRL/HGnufF4frdwwHwYDVR0jBBgwFoAUt6pk\n" + "zFt1PZlfYRL/HGnufF4frdwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC\n" + "AQEAld7Qwq0cdzDQ51w1RVLwTR8Oy25PB3rzwEHcSGJmdqlMi3xOdaz80S1R1BBX\n" + "ldvGBG5Tn0vT7xSuhmSgI2/HnBpy9ocHVOmhtNB4473NieEpfTYrnGXrFxu46Wus\n" + "9m/ZnugcQ2G6C54A/NFtvgLmaC8uH8M7gKdS6uYUwJFQEofkjmd4UpOYSqmcRXhS\n" + "Jzd5FYFWkJhKJYp3nlENSOD8CUFFVGekm05nFN2gRVc/qaqQkEX77+XYvhodLRsV\n" + "qMn7nf7taidDKLO2T4bhujztnTYOhhaXKgPy7AtZ28N2wvX96VyAPB/vrchGmyBK\n" + "kOg11TpPdNDkhb1J4ZCh2gupDg==\n" + "-----END CERTIFICATE-----\n"; + +// TEST_IM_CA, signed by TEST_ROOT_CA, expires 2035 +std::string TEST_IM_CA = + "-----BEGIN CERTIFICATE-----\n" + "MIIDljCCAn6gAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwZjELMAkGA1UEBhMCQVUx\n" + "EzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoMBEFDTUUxEDAOBgNVBAsMB1Rl\n" + "c3RpbmcxITAfBgNVBAMMGFRlc3Qgcm9vdCBjYSBjZXJ0aWZpY2F0ZTAeFw0xNTAx\n" + "MTYxNjQ1MzRaFw0zNTAxMTExNjQ1MzRaMGQxCzAJBgNVBAYTAkFVMRMwEQYDVQQI\n" + "DApTb21lLVN0YXRlMQ0wCwYDVQQKDARBQ01FMRAwDgYDVQQLDAdUZXN0aW5nMR8w\n" + "HQYDVQQDDBZUZXN0IElNIENBIGNlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEF\n" + "AAOCAQ8AMIIBCgKCAQEAzmBF78qClgoKfnLAncMXZwZ14TW+5kags1+QCYeg3c7j\n" + "L9+RvDxIaX2tKf1sukJcwQfYqUlQkwt+58LMOb2ORtkpj8Or6WCWCZ0BzneT8ug7\n" + "nxJT4m9+bohMF0JoKjjB2H4KNMHamLIwUxRKt6nyfk81kVhJOi2vzzxd+UCPi6Pc\n" + "UAbJNH48eNgOIg55nyFovVzYj8GIo/9GvHJj83PPa/KlJZ+Z1qZASZZ/VYorplVT\n" + "thsHXKfejhFy5YJ9t7n/vyAQsyBsagZsvX19xnH41fbYXHKf8UbXG23rNaZlchs6\n" + "XJVLQdzOpj3WTj/lCocVHqLaZISLhNQ3aI7kUBUdiwIDAQABo1AwTjAdBgNVHQ4E\n" + "FgQUoCYNaCBP4jl/3SYQuK8Ka+6i3QEwHwYDVR0jBBgwFoAUt6pkzFt1PZlfYRL/\n" + "HGnufF4frdwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjRzWiD97\n" + "Htv4Kxpm3P+C+xP9AEteCJfO+7p8MWgtWEJOknJyt55zeKS2JwZIq57KcbqD8U7v\n" + "vAUx1ymtUhlFPFd7J1mJ3pou+3aFYmGShYhGHpbrmUwjp7HVP588jrW1NoZVHdMc\n" + "4OgJWFrViXeu9+maIcekjMB/+9Y0dUgQuK5ZuT5H/Jwet7Th/o9uufTUZjBzRvrB\n" + "pbXgQpqgME2av4Q/6LuldPCTHLtWXgFUU2R+yCGmuGilvhFJnKoQryAbYnIQNWE8\n" + "SLoHQ9s1i7Zyb7HU6UAaqMOz15LBkyAqtNyJcO2p7Q/p5YK0xfD4xisI5qXucqVm\n" + "F2obL5qJSTN/RQ==\n" + "-----END CERTIFICATE-----\n"; + +// TEST_LEAF, signed by TEST_IM_CA, expires 2035 +std::string TEST_LEAF = + "-----BEGIN CERTIFICATE-----\n" + "MIIDOzCCAiMCAQEwDQYJKoZIhvcNAQEFBQAwZDELMAkGA1UEBhMCQVUxEzARBgNV\n" + "BAgMClNvbWUtU3RhdGUxDTALBgNVBAoMBEFDTUUxEDAOBgNVBAsMB1Rlc3Rpbmcx\n" + "HzAdBgNVBAMMFlRlc3QgSU0gQ0EgY2VydGlmaWNhdGUwHhcNMTUwMTE2MTY0ODE0\n" + "WhcNMzUwMTExMTY0ODE0WjBjMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1T\n" + "dGF0ZTENMAsGA1UECgwEQUNNRTEQMA4GA1UECwwHVGVzdGluZzEeMBwGA1UEAwwV\n" + "VGVzdCBsZWFmIGNlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + "CgKCAQEAzTdDIa2tDmRxFnIgiG+mBz8GoSVODs0ImNQGbqj+pLhBOFRH8fsah4Jl\n" + "z5YF9KwhMVLknnHGFLE/Nb7Ac35kEzhMQMpTRxohW83oxw3eZ8zN/FBoKqg4qHRq\n" + "QR8kS10YXTgrBR0ex/Vp+OUKEw6h7yL2r4Tpvrn9/qHwsxtLxqWbDIVf1O9b1Lfc\n" + "bllYMdmV5E62yN5tcwrDP8gvHjFnVeLzrG8wTpc9FR90/0Jkfp5jAJcArOBLrT0E\n" + "4VRqs+4HuwT8jAwFAmNnc7IYX5qSjtSWkmmHe73K/lzB+OiI0JEc/3eWUTWqwTSk\n" + "4tNCiQGBKJ39LXPTBBJdzmxVH7CUDQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAp\n" + "UdDOGu3hNiG+Vn10aQ6B1ZmOj3t+45gUV3sC+y8hB8EK1g4P5Ke9bVDts0T5eOnj\n" + "CSc+6VoND5O4adI0IFFRFljHNVnvjeosHfUZNnowsmA2ptQBtC1g5ZKRvKXlkC5/\n" + "i5BGgRqPFA7y9WB9Y05MrJHf3E+Oz/RBsLeeNiNN+rF5X1vYExvGHpo0M0zS0ze9\n" + "HtC0aOy8ocsTrQkf3ceHTAXx2i8ftoSSD4klojtWFpWMrNQa52F7wB9nU6FfKRuF\n" + "Zj/T1JkYXKkEwZU6nAR2jdZp3EP9xj3o15V/tyFcXHx6l8NTxn4cJb+Xe4VquQJz\n" + "6ON7PVe0ABN/AlwVQiFE\n" + "-----END CERTIFICATE-----\n"; +} // namespace anonymous + + +SchemeTest::SchemeTest() : m_userChanged(false) { + m_control = Control::create(); + m_mgr = Manager::create(); + + SmackAccess sa; + sa.add("System", LABEL, "rwx"); + sa.add(LABEL, "System", "rwx"); + sa.add(LABEL, "System::Run", "x"); + sa.apply(); +} + +SchemeTest::~SchemeTest() { + try { + SwitchToRoot(); + } catch (...) {} +} + +void SchemeTest::SwitchToUser() { + if (m_userChanged) + return; + + if(CKM_API_SUCCESS != m_control->unlockUserKey(UID, DBPASS)) + throw runtime_error("unlockUserKey failed"); + + // get calling label + char* label = NULL; + if (smack_new_label_from_self(&label) <= 0) + throw runtime_error("smack_new_label_from_self failed"); + + m_origLabel = string(label); + free(label); + + if(0 > smack_set_label_for_self(LABEL)) + throw runtime_error("smack_set_label_for_self failed"); + + if(0 > setegid(GID)) + throw runtime_error("setegid failed"); + + if(0 > seteuid(UID)) + throw runtime_error("seteuid failed"); + + m_userChanged = true; +} + +void SchemeTest::SwitchToRoot() { + if (!m_userChanged) + return; + + if(0 > seteuid(0)) + throw runtime_error("seteuid failed"); + if(0 > setegid(0)) + throw runtime_error("setegid failed"); + + if(0 > smack_set_label_for_self(m_origLabel.c_str())) + throw runtime_error("smack_set_label_for_self failed"); + + if(m_control->lockUserKey(UID) != CKM_API_SUCCESS) + throw runtime_error("lockUserKey failed"); +} + +void SchemeTest::FillDb() { + // pkcs + ifstream is("/usr/share/ckm-db-test/encryption-scheme.p12"); + if(!is) + throw runtime_error("Failed to read pkcs"); + istreambuf_iterator begin(is), end; + RawBuffer pkcsBuffer(begin, end); + auto pkcs = PKCS12::create(pkcsBuffer, Password()); + if(pkcs->empty()) + throw runtime_error("Empty pkcs"); + + SwitchToUser(); + + // certificates + RawBuffer rootCaBuffer(TEST_ROOT_CA.begin(), TEST_ROOT_CA.end()); + CertificateShPtr rootCa = CKM::Certificate::create(rootCaBuffer, CKM::DataFormat::FORM_PEM); + RawBuffer imCaBuffer(TEST_IM_CA.begin(), TEST_IM_CA.end()); + CertificateShPtr imCa = CKM::Certificate::create(imCaBuffer, CKM::DataFormat::FORM_PEM); + RawBuffer leafBuffer(TEST_LEAF.begin(), TEST_LEAF.end()); + CertificateShPtr leaf = CKM::Certificate::create(leafBuffer, CKM::DataFormat::FORM_PEM); + + for(const auto& g:GROUPS) { + switch (g.type) { + case Group::KEY_PAIR: + if(g.items.size() != 2) + throw runtime_error("Wrong number of keys"); + if( g.items[0].type != DataType::KEY_RSA_PRIVATE || + g.items[1].type != DataType::KEY_RSA_PUBLIC) + throw runtime_error("Invalid item type"); + + if(CKM_API_SUCCESS != m_mgr->createKeyPairRSA(1024, + g.items[0].alias, + g.items[1].alias, + g.items[0].policy, + g.items[1].policy)) + throw runtime_error("createKeyPair failed"); + break; + + case Group::CERT_CHAIN: + if(g.items.size() != CHAIN_SIZE) + throw runtime_error("Wrong number of certificates"); + if( g.items[0].type != DataType::CERTIFICATE || + g.items[1].type != DataType::CERTIFICATE || + g.items[2].type != DataType::CERTIFICATE) + throw runtime_error("Invalid item type"); + + if(CKM_API_SUCCESS != m_mgr->saveCertificate(g.items[0].alias, rootCa, g.items[0].policy)) + throw runtime_error("saveCertificate failed"); + if(CKM_API_SUCCESS != m_mgr->saveCertificate(g.items[1].alias, imCa, g.items[1].policy)) + throw runtime_error("saveCertificate failed"); + if(CKM_API_SUCCESS != m_mgr->saveCertificate(g.items[2].alias, leaf, g.items[2].policy)) + throw runtime_error("saveCertificate failed"); + break; + + default: + for(const auto& i:g.items) { + switch (i.type) { + case DataType::BINARY_DATA: + if(CKM_API_SUCCESS != m_mgr->saveData(i.alias, TEST_DATA, i.policy)) + throw runtime_error("saveData failed"); + break; + + case DataType::KEY_AES: + if(CKM_API_SUCCESS != m_mgr->createKeyAES(256, i.alias, i.policy)) + throw runtime_error("createKeyAES failed"); + break; + + case DataType::CHAIN_CERT_0: // PKCS + if(CKM_API_SUCCESS != m_mgr->savePKCS12(i.alias, pkcs, i.policy, i.policy)) + throw runtime_error("savePkcs12 failed"); + break; + + default: + throw runtime_error("unsupported data type"); + } + } + break; + } + } +} diff --git a/tests/encryption-scheme/scheme-test.h b/tests/encryption-scheme/scheme-test.h new file mode 100644 index 0000000..69edd24 --- /dev/null +++ b/tests/encryption-scheme/scheme-test.h @@ -0,0 +1,62 @@ +/* + * 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 scheme-test.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#pragma once + +#include + +#include +#include + +#include + +struct Item { + Item() : type(CKM::DataType::DB_FIRST){} + Item(const CKM::Alias& alias, + const CKM::DataType::Type type, + const CKM::Policy& policy) + : alias(alias), type(type), policy(policy) + { + } + + CKM::Alias alias; + CKM::DataType::Type type; + CKM::Policy policy; +}; + +typedef std::vector Items; + +class SchemeTest { +public: + SchemeTest(); + ~SchemeTest(); + + void FillDb(); + +private: + void SwitchToUser(); + void SwitchToRoot(); + + CKM::ControlShPtr m_control; + CKM::ManagerShPtr m_mgr; + std::string m_origLabel; + bool m_userChanged; +}; diff --git a/tests/encryption-scheme/smack-access.cpp b/tests/encryption-scheme/smack-access.cpp new file mode 100644 index 0000000..89e1bcb --- /dev/null +++ b/tests/encryption-scheme/smack-access.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2000 - 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 smack-access.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include + +#include + +#include + +SmackAccess::SmackAccess() : m_handle(nullptr) +{ + if(0 != smack_accesses_new(&m_handle)) + throw std::runtime_error("smack_accesses_new failed"); +} + +void SmackAccess::add( + const std::string &subject, + const std::string &object, + const std::string &rights) +{ + if(0 != smack_accesses_add(m_handle, subject.c_str(), object.c_str(), rights.c_str())) + throw std::runtime_error("smack_accesses_add failed"); +} + +void SmackAccess::apply() { + if(0 != smack_accesses_apply(m_handle)) + throw std::runtime_error("smack_accesses_apply failed"); +} + +SmackAccess::~SmackAccess() { + if (m_handle) + smack_accesses_free(m_handle); +} diff --git a/tests/encryption-scheme/smack-access.h b/tests/encryption-scheme/smack-access.h new file mode 100644 index 0000000..5fc740c --- /dev/null +++ b/tests/encryption-scheme/smack-access.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2000 - 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 smack-access.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#pragma once + +#include + +struct smack_accesses; + +class SmackAccess { +public: + SmackAccess(); + SmackAccess(const SmackAccess &second) = delete; + SmackAccess& operator=(const SmackAccess &second) = delete; + + void add(const std::string &subject, + const std::string &object, + const std::string &rights); + void apply(); + virtual ~SmackAccess(); +private: + struct smack_accesses *m_handle; +}; -- 2.7.4 From 51a5e9cfe81d0374a02c1e28375a482b68afd879 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Mon, 7 Sep 2015 13:19:54 +0200 Subject: [PATCH 09/16] Modify encryption scheme [Problem] Current encryption scheme makes it impossible to remove an entry protected with custom user password from database. [Solution] Encryption scheme modified. Store is responsible for encrypting data with user password. Service encrypts it with app key. Data encrypted with old scheme that is being read will be automatically reencrypted with new scheme. [Verification] Run tests from upcoming commit: ckm-tests-internal -t ENCRYPTION_SCHEME_TEST Change-Id: I8ed514290d9e75bbc89d74b006939e3cbb0b8bd2 --- src/manager/crypto/generic-backend/gobj.h | 2 + src/manager/crypto/generic-backend/gstore.h | 20 ++- src/manager/crypto/sw-backend/internals.cpp | 37 ++--- src/manager/crypto/sw-backend/internals.h | 21 ++- src/manager/crypto/sw-backend/obj.cpp | 4 - src/manager/crypto/sw-backend/obj.h | 2 +- src/manager/crypto/sw-backend/store.cpp | 146 ++++++++++++++++-- src/manager/crypto/sw-backend/store.h | 8 +- src/manager/crypto/tz-backend/store.cpp | 6 +- src/manager/crypto/tz-backend/store.h | 6 +- src/manager/service/ckm-logic.cpp | 221 +++++++++++++++++++--------- src/manager/service/ckm-logic.h | 19 ++- src/manager/service/crypto-logic.cpp | 66 ++++++--- src/manager/service/crypto-logic.h | 22 ++- src/manager/service/db-crypto.cpp | 45 ++++++ src/manager/service/db-crypto.h | 5 + 16 files changed, 480 insertions(+), 150 deletions(-) diff --git a/src/manager/crypto/generic-backend/gobj.h b/src/manager/crypto/generic-backend/gobj.h index 0857a0e..1e6598f 100644 --- a/src/manager/crypto/generic-backend/gobj.h +++ b/src/manager/crypto/generic-backend/gobj.h @@ -20,6 +20,7 @@ */ #pragma once #include +#include #include #include @@ -58,6 +59,7 @@ public: typedef std::unique_ptr GObjUPtr; typedef std::shared_ptr GObjShPtr; +typedef std::vector GObjUPtrVector; } // namespace Crypto } // namespace CKM diff --git a/src/manager/crypto/generic-backend/gstore.h b/src/manager/crypto/generic-backend/gstore.h index 30e177d..bcb3a57 100644 --- a/src/manager/crypto/generic-backend/gstore.h +++ b/src/manager/crypto/generic-backend/gstore.h @@ -33,11 +33,21 @@ namespace Crypto { class GStore { public: - virtual GObjUPtr getObject(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported); } - virtual TokenPair generateAKey(const CryptoAlgorithm &) { ThrowErr(Exc::Crypto::OperationNotSupported); } - virtual Token generateSKey(const CryptoAlgorithm &) { ThrowErr(Exc::Crypto::OperationNotSupported); } - virtual Token import(DataType, const RawBuffer &) { ThrowErr(Exc::Crypto::OperationNotSupported); } - virtual void destroy(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported); } + virtual GObjUPtr getObject(const Token &, const Password &) { + ThrowErr(Exc::Crypto::OperationNotSupported); + } + virtual TokenPair generateAKey(const CryptoAlgorithm &, const Password &, const Password &) { + ThrowErr(Exc::Crypto::OperationNotSupported); + } + virtual Token generateSKey(const CryptoAlgorithm &, const Password &) { + ThrowErr(Exc::Crypto::OperationNotSupported); + } + virtual Token import(DataType, const RawBuffer &, const Password &) { + ThrowErr(Exc::Crypto::OperationNotSupported); + } + virtual void destroy(const Token &) { + ThrowErr(Exc::Crypto::OperationNotSupported); + } virtual ~GStore() {} protected: diff --git a/src/manager/crypto/sw-backend/internals.cpp b/src/manager/crypto/sw-backend/internals.cpp index d5dfa23..6239c66 100644 --- a/src/manager/crypto/sw-backend/internals.cpp +++ b/src/manager/crypto/sw-backend/internals.cpp @@ -368,7 +368,7 @@ int getRsaPadding(const RSAPaddingAlgorithm padAlgo) { return rsa_padding; } -TokenPair createKeyPairRSA(CryptoBackend backendId, const int size) +DataPair createKeyPairRSA(const int size) { EvpPkeyUPtr pkey; @@ -396,12 +396,13 @@ TokenPair createKeyPairRSA(CryptoBackend backendId, const int size) } pkey = EvpPkeyUPtr(pkeyTmp, EVP_PKEY_free); - return std::make_pair(Token(backendId, DataType(KeyType::KEY_RSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())), - Token(backendId, DataType(KeyType::KEY_RSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get()))); + return std::make_pair( + {DataType(KeyType::KEY_RSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())}, + {DataType(KeyType::KEY_RSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())}); } -TokenPair createKeyPairDSA(CryptoBackend backendId, const int size) +DataPair createKeyPairDSA(const int size) { EvpPkeyUPtr pkey; EvpPkeyUPtr pparam; @@ -449,11 +450,12 @@ TokenPair createKeyPairDSA(CryptoBackend backendId, const int size) } pkey = EvpPkeyUPtr(pkeyTmp, EVP_PKEY_free); - return std::make_pair(Token(backendId, DataType(KeyType::KEY_DSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())), - Token(backendId, DataType(KeyType::KEY_DSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get()))); + return std::make_pair( + {DataType(KeyType::KEY_DSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())}, + {DataType(KeyType::KEY_DSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())}); } -TokenPair createKeyPairECDSA(CryptoBackend backendId, ElipticCurve type) +DataPair createKeyPairECDSA(ElipticCurve type) { int ecCurve = NOT_DEFINED; EvpPkeyUPtr pkey; @@ -511,11 +513,12 @@ TokenPair createKeyPairECDSA(CryptoBackend backendId, ElipticCurve type) } pkey = EvpPkeyUPtr(pkeyTmp, EVP_PKEY_free); - return std::make_pair(Token(backendId, DataType(KeyType::KEY_ECDSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())), - Token(backendId, DataType(KeyType::KEY_ECDSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get()))); + return std::make_pair( + {DataType(KeyType::KEY_ECDSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())}, + {DataType(KeyType::KEY_ECDSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())}); } -Token createKeyAES(CryptoBackend backendId, const int sizeBits) +Data createKeyAES(const int sizeBits) { // check the parameters of functions if(sizeBits!=128 && sizeBits!=192 && sizeBits!=256) { @@ -530,10 +533,10 @@ Token createKeyAES(CryptoBackend backendId, const int sizeBits) ThrowMsg(Exc::Crypto::InternalError, "Error in AES key generation"); } - return Token(backendId, DataType(KeyType::KEY_AES), CKM::RawBuffer(key, key+sizeBytes)); + return { DataType(KeyType::KEY_AES), CKM::RawBuffer(key, key+sizeBytes)}; } -TokenPair generateAKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm) +DataPair generateAKey(const CryptoAlgorithm &algorithm) { validateParams(algorithm); @@ -542,23 +545,23 @@ TokenPair generateAKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm { int keyLength = unpack(algorithm, ParamName::GEN_KEY_LEN); if(keyType == AlgoType::RSA_GEN) - return createKeyPairRSA(backendId, keyLength); + return createKeyPairRSA(keyLength); else - return createKeyPairDSA(backendId, keyLength); + return createKeyPairDSA(keyLength); } else // AlgoType::ECDSA_GEN { ElipticCurve ecType = unpack(algorithm, ParamName::GEN_EC); - return createKeyPairECDSA(backendId, ecType); + return createKeyPairECDSA(ecType); } } -Token generateSKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm) +Data generateSKey(const CryptoAlgorithm &algorithm) { validateParams(algorithm); int keySizeBits = unpack(algorithm, ParamName::GEN_KEY_LEN); - return createKeyAES(backendId, keySizeBits); + return createKeyAES(keySizeBits); } RawBuffer encryptDataAes( diff --git a/src/manager/crypto/sw-backend/internals.h b/src/manager/crypto/sw-backend/internals.h index 3b54394..ecf52d4 100644 --- a/src/manager/crypto/sw-backend/internals.h +++ b/src/manager/crypto/sw-backend/internals.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #define EVP_SUCCESS 1 // DO NOTCHANGE THIS VALUE @@ -40,13 +39,21 @@ namespace Crypto { namespace SW { namespace Internals { -TokenPair createKeyPairRSA(CryptoBackend backendId, const int size); -TokenPair createKeyPairDSA(CryptoBackend backendId, const int size); -TokenPair createKeyPairECDSA(CryptoBackend backendId, ElipticCurve type1); -Token createKeyAES(CryptoBackend backendId, const int sizeBits); +// TODO replace it with DataContainer +struct Data { + DataType type; + RawBuffer buffer; +}; -TokenPair generateAKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm); -Token generateSKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm); +typedef std::pair DataPair; + +DataPair createKeyPairRSA(const int size); +DataPair createKeyPairDSA(const int size); +DataPair createKeyPairECDSA(ElipticCurve type1); +Data createKeyAES(const int sizeBits); + +DataPair generateAKey(const CryptoAlgorithm &algorithm); +Data generateSKey(const CryptoAlgorithm &algorithm); RawBuffer symmetricEncrypt(const RawBuffer &key, const CryptoAlgorithm &alg, diff --git a/src/manager/crypto/sw-backend/obj.cpp b/src/manager/crypto/sw-backend/obj.cpp index 0b602a3..2a70736 100644 --- a/src/manager/crypto/sw-backend/obj.cpp +++ b/src/manager/crypto/sw-backend/obj.cpp @@ -59,10 +59,6 @@ AlgoType key2algo(DataType type) { typedef std::unique_ptr> BioUniquePtr; -RawBuffer BData::getBinary() const { - return m_raw; -} - RawBuffer SKey::encrypt(const CryptoAlgorithm &alg, const RawBuffer &data) { return Internals::symmetricEncrypt(getBinary(), alg, data); diff --git a/src/manager/crypto/sw-backend/obj.h b/src/manager/crypto/sw-backend/obj.h index c56354f..f8c3cfe 100644 --- a/src/manager/crypto/sw-backend/obj.h +++ b/src/manager/crypto/sw-backend/obj.h @@ -40,7 +40,7 @@ public: , m_type(keyType) {} - virtual RawBuffer getBinary() const; + virtual RawBuffer getBinary() const { return m_raw; } protected: RawBuffer m_raw; DataType m_type; diff --git a/src/manager/crypto/sw-backend/store.cpp b/src/manager/crypto/sw-backend/store.cpp index 0c6669a..4e944ad 100644 --- a/src/manager/crypto/sw-backend/store.cpp +++ b/src/manager/crypto/sw-backend/store.cpp @@ -20,66 +20,180 @@ */ #include +#include +#include + #include #include #include #include +#include + +namespace CKM { +namespace Crypto { +namespace SW { + namespace { +const int ITERATIONS = 1024; +const int KEY_LENGTH = 16; // length of AES key derived from password +const int STORE_AES_GCM_TAG_SIZE = 16; // length of AES GCM tag + +// internal SW encryption scheme flags +enum EncryptionScheme { + NONE = 0, + PASSWORD = 1 << 0 +}; + template std::unique_ptr make_unique(Args&& ...args) { return std::unique_ptr(new T(std::forward(args)...)); } -} // namespace anonymous +RawBuffer generateRandIV() { + RawBuffer civ(EVP_MAX_IV_LENGTH); -namespace CKM { -namespace Crypto { -namespace SW { + if (1 != RAND_bytes(civ.data(), civ.size())) + ThrowErr(Exc::Crypto::InternalError, "RAND_bytes failed to generate IV."); + return civ; +} + +RawBuffer passwordToKey(const Password &password, const RawBuffer &salt, size_t keySize) +{ + RawBuffer result(keySize); + + if (1 != PKCS5_PBKDF2_HMAC_SHA1( + password.c_str(), + password.size(), + salt.data(), + salt.size(), + ITERATIONS, + result.size(), + result.data())) + { + ThrowErr(Exc::InternalError, "PCKS5_PKKDF2_HMAC_SHA1 failed."); + } + + return result; +} + +RawBuffer unpack(const RawBuffer& packed, const Password& pass) +{ + MessageBuffer buffer; + buffer.Push(packed); + int encryptionScheme = 0; + RawBuffer data; + buffer.Deserialize(encryptionScheme, data); + + if (encryptionScheme == 0) + return data; + + MessageBuffer internalBuffer; + internalBuffer.Push(data); + RawBuffer encrypted; + RawBuffer iv; + RawBuffer tag; + + // serialization exceptions will be catched as CKM::Exception and will cause + // CKM_API_ERROR_SERVER_ERROR + internalBuffer.Deserialize(encrypted, iv, tag); + + /* + * AES GCM will check data integrity and handle cases where: + * - wrong password is used + * - password is empty when it shouldn't be + * - password is not empty when it should be + */ + RawBuffer key = passwordToKey(pass, iv, KEY_LENGTH); + + RawBuffer ret; + try { + ret = Crypto::SW::Internals::decryptDataAesGcm(key, encrypted, iv, tag); + } catch( const Exc::Crypto::InternalError& e) { + ThrowErr(Exc::AuthenticationFailed, "Decryption with custom password failed"); + } + return ret; +} + +RawBuffer pack(const RawBuffer& data, const Password& pass) +{ + int scheme = EncryptionScheme::NONE; + RawBuffer packed = data; + if (!pass.empty()) { + RawBuffer iv = generateRandIV(); + RawBuffer key = passwordToKey(pass, iv, KEY_LENGTH); + + std::pair ret; + try { + ret = Crypto::SW::Internals::encryptDataAesGcm(key, data, iv, STORE_AES_GCM_TAG_SIZE); + } catch( const Exc::Crypto::InternalError& e) { + ThrowErr(Exc::AuthenticationFailed, "Encryption with custom password failed"); + } + scheme |= EncryptionScheme::PASSWORD; + + // serialization exceptions will be catched as CKM::Exception and will cause + // CKM_API_ERROR_SERVER_ERROR + packed = MessageBuffer::Serialize(ret.first, iv, ret.second).Pop(); + } + // encryption scheme + internal buffer + return MessageBuffer::Serialize(scheme, packed).Pop(); +} + +} // namespace anonymous Store::Store(CryptoBackend backendId) : GStore(backendId) { } -GObjUPtr Store::getObject(const Token &token) { +GObjUPtr Store::getObject(const Token &token, const Password &pass) { if (token.backendId != m_backendId) { ThrowErr(Exc::Crypto::WrongBackend, "Decider choose wrong backend!"); } + RawBuffer data = unpack(token.data, pass); + if (token.dataType.isKeyPrivate() || token.dataType.isKeyPublic()) { - return make_unique(token.data, token.dataType); + return make_unique(data, token.dataType); } if (token.dataType == DataType(DataType::KEY_AES)) { - return make_unique(token.data, token.dataType); + return make_unique(data, token.dataType); } - if (token.dataType.isCertificate()) { - return make_unique(token.data, token.dataType); + if (token.dataType.isCertificate() || token.dataType.isChainCert()) { + return make_unique(data, token.dataType); } if (token.dataType.isBinaryData()) { - return make_unique(token.data, token.dataType); + return make_unique(data, token.dataType); } ThrowErr(Exc::Crypto::DataTypeNotSupported, "This type of data is not supported by openssl backend: ", (int)token.dataType); } -TokenPair Store::generateAKey(const CryptoAlgorithm &algorithm) +TokenPair Store::generateAKey(const CryptoAlgorithm &algorithm, + const Password &prvPass, + const Password &pubPass) { - return Internals::generateAKey(m_backendId, algorithm); + Internals::DataPair ret = Internals::generateAKey(algorithm); + return std::make_pair( + Token(m_backendId, ret.first.type, pack(ret.first.buffer, prvPass)), + Token(m_backendId, ret.second.type, pack(ret.second.buffer, pubPass))); } -Token Store::generateSKey(const CryptoAlgorithm &algorithm) +Token Store::generateSKey(const CryptoAlgorithm &algorithm, const Password &pass) { - return Internals::generateSKey(m_backendId, algorithm); + Internals::Data ret = Internals::generateSKey(algorithm); + return Token(m_backendId, ret.type, pack(ret.buffer, pass)); } -Token Store::import(DataType dataType, const RawBuffer &buffer) { - return Token(m_backendId, dataType, buffer); +Token Store::import(DataType dataType, const RawBuffer &input, const Password &pass) { + + RawBuffer data = pack(input, pass); + return Token(m_backendId, dataType, std::move(data)); } } // namespace SW diff --git a/src/manager/crypto/sw-backend/store.h b/src/manager/crypto/sw-backend/store.h index 552f5bc..f39d0c5 100644 --- a/src/manager/crypto/sw-backend/store.h +++ b/src/manager/crypto/sw-backend/store.h @@ -31,10 +31,10 @@ class Store : public GStore { public: explicit Store(CryptoBackend backendId); - virtual GObjUPtr getObject(const Token &token); - virtual TokenPair generateAKey(const CryptoAlgorithm &); - virtual Token generateSKey(const CryptoAlgorithm &); - virtual Token import(DataType dataType, const RawBuffer &buffer); + virtual GObjUPtr getObject(const Token &, const Password &); + virtual TokenPair generateAKey(const CryptoAlgorithm &, const Password &, const Password &); + virtual Token generateSKey(const CryptoAlgorithm &, const Password &); + virtual Token import(DataType, const RawBuffer &, const Password &); virtual void destroy(const Token &){} }; diff --git a/src/manager/crypto/tz-backend/store.cpp b/src/manager/crypto/tz-backend/store.cpp index fe3fae7..1c5d58a 100644 --- a/src/manager/crypto/tz-backend/store.cpp +++ b/src/manager/crypto/tz-backend/store.cpp @@ -30,15 +30,15 @@ Store::Store(CryptoBackend backendId) : GStore(backendId) {} -GObjUPtr Store::getObject(const Token &) { +GObjUPtr Store::getObject(const Token &, const Password &) { ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!"); } -TokenPair Store::generateAKey(const CryptoAlgorithm &) { +TokenPair Store::generateAKey(const CryptoAlgorithm &, const Password &, const Password &) { ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!"); } -Token Store::import(DataType, const RawBuffer &) { +Token Store::import(DataType, const RawBuffer &, const Password &) { ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!"); } diff --git a/src/manager/crypto/tz-backend/store.h b/src/manager/crypto/tz-backend/store.h index be3595c..85e193d 100644 --- a/src/manager/crypto/tz-backend/store.h +++ b/src/manager/crypto/tz-backend/store.h @@ -31,9 +31,9 @@ class Store : public GStore { public: explicit Store(CryptoBackend backendId); - virtual GObjUPtr getObject(const Token &token); - virtual TokenPair generateAKey(const CryptoAlgorithm &); - virtual Token import(DataType dataType, const RawBuffer &buffer); + virtual GObjUPtr getObject(const Token &, const Password &); + virtual TokenPair generateAKey(const CryptoAlgorithm &, const Password &, const Password &); + virtual Token import(DataType dataType, const RawBuffer &buffer, const Password &); virtual void destroy(const Token &){} }; diff --git a/src/manager/service/ckm-logic.cpp b/src/manager/service/ckm-logic.cpp index 728bfc5..8736fd5 100644 --- a/src/manager/service/ckm-logic.cpp +++ b/src/manager/service/ckm-logic.cpp @@ -418,16 +418,11 @@ DB::Row CKMLogic::createEncryptedRow( const Policy &policy) const { Crypto::GStore& store = m_decider.getStore(dataType, policy.extractable); - Token token = store.import(dataType, data); - - DB::Row row(std::move(token), name, label, static_cast(policy.extractable)); // do not encrypt data with password during cc_mode on - if(m_accessControl.isCCMode()) { - crypto.encryptRow("", row); - } else { - crypto.encryptRow(policy.password, row); - } + Token token = store.import(dataType, data, m_accessControl.isCCMode() ? "" : policy.password); + DB::Row row(std::move(token), name, label, static_cast(policy.extractable)); + crypto.encryptRow(row); return row; } @@ -515,9 +510,10 @@ int CKMLogic::getKeyForService( DB::Row row; try { // Key is for internal service use. It won't be exported to the client - int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, label, pass, row); + Crypto::GObjUPtr obj; + int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, label, pass, obj); if (retCode == CKM_API_SUCCESS) - key = m_decider.getStore(row).getObject(row); + key = std::move(obj); return retCode; } catch (const DB::Crypto::Exception::Base &e) { LogError("DB::Crypto failed with message: " << e.GetMessage()); @@ -666,17 +662,12 @@ int CKMLogic::removeDataHelper( // destroy it in store for(auto& r : rows) { - /* - * TODO: If row is encrypted with user password we won't be able to decrypt it (tz id). - * Encryption/decryption with user password and with app key should both be done inside the - * store (import, getKey and generateXKey). - */ try { handler.crypto.decryptRow(Password(), r); + m_decider.getStore(r).destroy(r); } catch (const Exc::AuthenticationFailed&) { LogDebug("Authentication failed when removing data. Ignored."); } - m_decider.getStore(r.dataType, r.exportable).destroy(r); } // delete row in db @@ -803,6 +794,45 @@ int CKMLogic::checkDataPermissionsHelper(const Credentials &cred, return m_accessControl.canRead(cred, PermissionForLabel(accessorLabel, permissionRowOpt)); } +Crypto::GObjUPtr CKMLogic::rowToObject( + UserData& handler, + DB::Row row, + const Password& password) +{ + Crypto::GStore& store = m_decider.getStore(row); + + Password pass = m_accessControl.isCCMode() ? "" : password; + + // decrypt row + Crypto::GObjUPtr obj; + if(CryptoLogic::getSchemeVersion(row.encryptionScheme) == CryptoLogic::ENCRYPTION_V2) { + handler.crypto.decryptRow(Password(), row); + + obj = store.getObject(row, pass); + } else { + // decrypt entirely with old scheme: b64(pass(appkey(data))) -> data + handler.crypto.decryptRow(pass, row); + // destroy it in store + store.destroy(row); + + // import it to store with new scheme: data -> pass(data) + Token token = store.import(row.dataType,row.data, pass); + + // get it from the store (it can be different than the data we imported into store) + obj = store.getObject(token, pass); + + // update row with new token + *static_cast(&row) = std::move(token); + + // encrypt it with app key: pass(data) -> b64(appkey(pass(data)) + handler.crypto.encryptRow(row); + + // update it in db + handler.database.updateRow(row); + } + return obj; +} + int CKMLogic::readDataHelper( bool exportFlag, const Credentials &cred, @@ -810,7 +840,7 @@ int CKMLogic::readDataHelper( const Name &name, const Label &label, const Password &password, - DB::RowVector &rows) + Crypto::GObjUPtrVector &objs) { auto &handler = selectDatabase(cred, label); @@ -822,6 +852,7 @@ int CKMLogic::readDataHelper( // read rows DB::Crypto::Transaction transaction(&handler.database); + DB::RowVector rows; int retCode = readMultiRow(name, ownerLabel, dataType, handler.database, rows); if(CKM_API_SUCCESS != retCode) return retCode; @@ -841,7 +872,9 @@ int CKMLogic::readDataHelper( // decrypt row for(auto &row : rows) - handler.crypto.decryptRow(password, row); + objs.push_back(rowToObject(handler, std::move(row), password)); + // rowToObject may modify db + transaction.commit(); return CKM_API_SUCCESS; } @@ -853,7 +886,21 @@ int CKMLogic::readDataHelper( const Name &name, const Label &label, const Password &password, - DB::Row &row) + Crypto::GObjUPtr &obj) +{ + DataType objDataType; + return readDataHelper(exportFlag, cred, dataType, name, label, password, obj, objDataType); +} + +int CKMLogic::readDataHelper( + bool exportFlag, + const Credentials &cred, + DataType dataType, + const Name &name, + const Label &label, + const Password &password, + Crypto::GObjUPtr &obj, + DataType& objDataType) { auto &handler = selectDatabase(cred, label); @@ -865,10 +912,13 @@ int CKMLogic::readDataHelper( // read row DB::Crypto::Transaction transaction(&handler.database); + DB::Row row; int retCode = readSingleRow(name, ownerLabel, dataType, handler.database, row); if(CKM_API_SUCCESS != retCode) return retCode; + objDataType = row.dataType; + // check access rights retCode = checkDataPermissionsHelper(cred, name, ownerLabel, cred.smackLabel, row, exportFlag, handler.database); if(CKM_API_SUCCESS != retCode) @@ -879,8 +929,9 @@ int CKMLogic::readDataHelper( if(CKM_API_SUCCESS != retCode) return retCode; - // decrypt row - handler.crypto.decryptRow(password, row); + obj = rowToObject(handler, std::move(row), password); + // rowToObject may modify db + transaction.commit(); return CKM_API_SUCCESS; } @@ -895,9 +946,13 @@ RawBuffer CKMLogic::getData( { int retCode = CKM_API_SUCCESS; DB::Row row; + DataType objDataType; try { - retCode = readDataHelper(true, cred, dataType, name, label, password, row); + Crypto::GObjUPtr obj; + retCode = readDataHelper(true, cred, dataType, name, label, password, obj, objDataType); + if(retCode == CKM_API_SUCCESS) + row.data = std::move(obj->getBinary()); } catch (const DB::Crypto::Exception::Base &e) { LogError("DB::Crypto failed with message: " << e.GetMessage()); retCode = CKM_API_ERROR_DB_ERROR; @@ -916,7 +971,7 @@ RawBuffer CKMLogic::getData( auto response = MessageBuffer::Serialize(static_cast(LogicCommand::GET), commandId, retCode, - static_cast(row.dataType), + static_cast(objDataType), row.data); return response.Pop(); } @@ -934,27 +989,27 @@ int CKMLogic::getPKCS12Helper( int retCode; // read private key (mandatory) - DB::Row privKeyRow; - retCode = readDataHelper(true, cred, DataType::DB_KEY_FIRST, name, label, keyPassword, privKeyRow); + Crypto::GObjUPtr keyObj; + retCode = readDataHelper(true, cred, DataType::DB_KEY_FIRST, name, label, keyPassword, keyObj); if(retCode != CKM_API_SUCCESS) return retCode; - privKey = CKM::Key::create(privKeyRow.data); + privKey = CKM::Key::create(keyObj->getBinary()); // read certificate (mandatory) - DB::Row certRow; - retCode = readDataHelper(true, cred, DataType::CERTIFICATE, name, label, certPassword, certRow); + Crypto::GObjUPtr certObj; + retCode = readDataHelper(true, cred, DataType::CERTIFICATE, name, label, certPassword, certObj); if(retCode != CKM_API_SUCCESS) return retCode; - cert = CKM::Certificate::create(certRow.data, DataFormat::FORM_DER); + cert = CKM::Certificate::create(certObj->getBinary(), DataFormat::FORM_DER); // read CA cert chain (optional) - DB::RowVector rawCaChain; - retCode = readDataHelper(true, cred, DataType::DB_CHAIN_FIRST, name, label, certPassword, rawCaChain); + Crypto::GObjUPtrVector caChainObjs; + retCode = readDataHelper(true, cred, DataType::DB_CHAIN_FIRST, name, label, certPassword, caChainObjs); if(retCode != CKM_API_SUCCESS && retCode != CKM_API_ERROR_DB_ALIAS_UNKNOWN) return retCode; - for(auto &rawCaCert : rawCaChain) - caChain.push_back(CKM::Certificate::create(rawCaCert.data, DataFormat::FORM_DER)); + for(auto &caCertObj : caChainObjs) + caChain.push_back(CKM::Certificate::create(caCertObj->getBinary(), DataFormat::FORM_DER)); // if anything found, return it if(privKey || cert || caChain.size()>0) @@ -1157,17 +1212,33 @@ int CKMLogic::createKeyAESHelper( const Label &label, const PolicySerializable &policy) { + auto &handler = selectDatabase(cred, label); + + // use client label if not explicitly provided + const Label &ownerLabel = label.empty() ? cred.smackLabel : label; + if( m_accessControl.isSystemService(cred) && ownerLabel.compare(OWNER_ID_SYSTEM)!=0) + return CKM_API_ERROR_INPUT_PARAM; + + // check if save is possible + DB::Crypto::Transaction transaction(&handler.database); + int retCode = checkSaveConditions(cred, handler, name, ownerLabel); + if(retCode != CKM_API_SUCCESS) + return retCode; + + // create key in store CryptoAlgorithm keyGenAlgorithm; keyGenAlgorithm.setParam(ParamName::ALGO_TYPE, AlgoType::AES_GEN); keyGenAlgorithm.setParam(ParamName::GEN_KEY_LEN, size); - Token key = m_decider.getStore(DataType::KEY_AES, policy.extractable).generateSKey(keyGenAlgorithm); - - return saveDataHelper(cred, - name, - label, - DataType::KEY_AES, - key.data, - policy); + Token key = m_decider.getStore(DataType::KEY_AES, policy.extractable).generateSKey(keyGenAlgorithm, policy.password); + + // save the data + DB::Row row(std::move(key), name, ownerLabel, static_cast(policy.extractable)); + handler.crypto.encryptRow(row); + + handler.database.saveRow(row); + + transaction.commit(); + return CKM_API_SUCCESS; } @@ -1191,32 +1262,41 @@ int CKMLogic::createKeyPairHelper( if(!dt.isKey()) ThrowErr(Exc::InputParam, "Error, parameter ALGO_TYPE with wrong value."); + // use client label if not explicitly provided + const Label &ownerLabelPrv = labelPrivate.empty() ? cred.smackLabel : labelPrivate; + if( m_accessControl.isSystemService(cred) && ownerLabelPrv.compare(OWNER_ID_SYSTEM)!=0) + return CKM_API_ERROR_INPUT_PARAM; + const Label &ownerLabelPub = labelPublic.empty() ? cred.smackLabel : labelPublic; + if( m_accessControl.isSystemService(cred) && ownerLabelPub.compare(OWNER_ID_SYSTEM)!=0) + return CKM_API_ERROR_INPUT_PARAM; + bool exportable = policyPrivate.extractable || policyPublic.extractable; - TokenPair keys = m_decider.getStore(dt, exportable).generateAKey(keyGenParams); + TokenPair keys = m_decider.getStore(dt, exportable).generateAKey(keyGenParams, + policyPrivate.password, + policyPublic.password); DB::Crypto::Transaction transactionPriv(&handlerPriv.database); // in case the same database is used for private and public - the second // transaction will not be executed DB::Crypto::Transaction transactionPub(&handlerPub.database); - int retCode = saveDataHelper(cred, - namePrivate, - labelPrivate, - keys.first.dataType, - keys.first.data, - policyPrivate); - if (CKM_API_SUCCESS != retCode) + int retCode; + retCode = checkSaveConditions(cred, handlerPriv, namePrivate, ownerLabelPrv); + if(retCode != CKM_API_SUCCESS) return retCode; - - retCode = saveDataHelper(cred, - namePublic, - labelPublic, - keys.second.dataType, - keys.second.data, - policyPublic); - if (CKM_API_SUCCESS != retCode) + retCode = checkSaveConditions(cred, handlerPub, namePrivate, ownerLabelPub); + if(retCode != CKM_API_SUCCESS) return retCode; + // save the data + DB::Row rowPrv(std::move(keys.first), namePrivate, ownerLabelPrv, static_cast(policyPrivate.extractable)); + handlerPriv.crypto.encryptRow(rowPrv); + handlerPriv.database.saveRow(rowPrv); + + DB::Row rowPub(std::move(keys.second), namePublic, ownerLabelPub, static_cast(policyPublic.extractable)); + handlerPub.crypto.encryptRow(rowPub); + handlerPub.database.saveRow(rowPub); + transactionPub.commit(); transactionPriv.commit(); return CKM_API_SUCCESS; @@ -1301,18 +1381,21 @@ int CKMLogic::readCertificateHelper( { DB::Row row; for (auto &i: labelNameVector) { - int ec = readDataHelper(false, cred, DataType::CERTIFICATE, i.second, i.first, Password(), row); + // certificates can't be protected with custom user password + Crypto::GObjUPtr obj; + int ec = readDataHelper(false, cred, DataType::CERTIFICATE, i.second, i.first, Password(), obj); if (ec != CKM_API_SUCCESS) return ec; - certVector.push_back(CertificateImpl(row.data, DataFormat::FORM_DER)); + + certVector.emplace_back(obj->getBinary(), DataFormat::FORM_DER); // try to read chain certificates (if present) - DB::RowVector rawCaChain; - ec = readDataHelper(false, cred, DataType::DB_CHAIN_FIRST, i.second, i.first, CKM::Password(), rawCaChain); + Crypto::GObjUPtrVector caChainObjs; + ec = readDataHelper(false, cred, DataType::DB_CHAIN_FIRST, i.second, i.first, CKM::Password(), caChainObjs); if(ec != CKM_API_SUCCESS && ec != CKM_API_ERROR_DB_ALIAS_UNKNOWN) return ec; - for(auto &rawCaCert : rawCaChain) - certVector.push_back(CertificateImpl(rawCaCert.data, DataFormat::FORM_DER)); + for(auto &caCertObj : caChainObjs) + certVector.emplace_back(caCertObj->getBinary(), DataFormat::FORM_DER); } return CKM_API_SUCCESS; } @@ -1490,9 +1573,10 @@ RawBuffer CKMLogic::createSignature( int retCode = CKM_API_SUCCESS; try { - retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, privateKeyName, ownerLabel, password, row); + Crypto::GObjUPtr obj; + retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, privateKeyName, ownerLabel, password, obj); if(retCode == CKM_API_SUCCESS) { - signature = m_decider.getStore(row).getObject(row)->sign(cryptoAlg, message); + signature = obj->sign(cryptoAlg, message); } } catch (const DB::Crypto::Exception::Base &e) { LogError("DB::Crypto failed with message: " << e.GetMessage()); @@ -1534,13 +1618,14 @@ RawBuffer CKMLogic::verifySignature( // try certificate first - looking for a public key. // in case of PKCS, pub key from certificate will be found first // rather than private key from the same PKCS. - retCode = readDataHelper(false, cred, DataType::CERTIFICATE, publicKeyOrCertName, ownerLabel, password, row); + Crypto::GObjUPtr obj; + retCode = readDataHelper(false, cred, DataType::CERTIFICATE, publicKeyOrCertName, ownerLabel, password, obj); if (retCode == CKM_API_ERROR_DB_ALIAS_UNKNOWN) { - retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, publicKeyOrCertName, ownerLabel, password, row); + retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, publicKeyOrCertName, ownerLabel, password, obj); } if (retCode == CKM_API_SUCCESS) { - retCode = m_decider.getStore(row).getObject(row)->verify(params, message, signature); + retCode = obj->verify(params, message, signature); } } catch (const Exc::Exception &e) { retCode = e.error(); diff --git a/src/manager/service/ckm-logic.h b/src/manager/service/ckm-logic.h index c07225b..d3f0c40 100644 --- a/src/manager/service/ckm-logic.h +++ b/src/manager/service/ckm-logic.h @@ -314,6 +314,11 @@ private: bool exportFlag, DB::Crypto & database); + Crypto::GObjUPtr rowToObject( + UserData& handler, + DB::Row row, + const Password& password); + int readDataHelper( bool exportFlag, const Credentials &cred, @@ -321,7 +326,17 @@ private: const Name &name, const Label &label, const Password &password, - DB::Row &row); + Crypto::GObjUPtr &obj); + + int readDataHelper( + bool exportFlag, + const Credentials &cred, + DataType dataType, + const Name &name, + const Label &label, + const Password &password, + Crypto::GObjUPtr &obj, + DataType& objDataType); int readDataHelper( bool exportFlag, @@ -330,7 +345,7 @@ private: const Name &name, const Label &label, const Password &password, - DB::RowVector &rows); + Crypto::GObjUPtrVector &objs); int createKeyAESHelper( const Credentials &cred, diff --git a/src/manager/service/crypto-logic.cpp b/src/manager/service/crypto-logic.cpp index 9f663c8..c7b8786 100644 --- a/src/manager/service/crypto-logic.cpp +++ b/src/manager/service/crypto-logic.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -41,14 +42,34 @@ #include #include +namespace CKM { + namespace { const static int AES_CBC_KEY_SIZE = 32; const static int AES_GCM_TAG_SIZE = 16; -} // anonymous namespace +// Encryption scheme flags (enable/disable specific encryption type, multiple choice) +const int ENCR_BASE64 = 1 << 0; +const int ENCR_APPKEY = 1 << 1; +const int ENCR_PASSWORD = 1 << 2; -namespace CKM { +// Encryption order flags (single choice) +const int ENCR_ORDER_OFFSET = 24; +const int ENCR_ORDER_FILTER = INT_MAX << ENCR_ORDER_OFFSET; // 0xff000000 +const int ENCR_ORDER_CLEAR = ~ENCR_ORDER_FILTER; // 0x00ffffff +/* + * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and + * optionally by custom user password. In such form it is stored in db. + */ +const int ENCR_ORDER_V1 = CryptoLogic::ENCRYPTION_V1 << ENCR_ORDER_OFFSET; +/* + * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with + * user password. Returned token is encrypted with app key and stored in db. + */ +const int ENCR_ORDER_V2 = CryptoLogic::ENCRYPTION_V2 << ENCR_ORDER_OFFSET; + +} // anonymous namespace CryptoLogic::CryptoLogic() {} @@ -122,7 +143,7 @@ RawBuffer CryptoLogic::generateRandIV() const { return civ; } -void CryptoLogic::encryptRow(const Password &password, DB::Row &row) +void CryptoLogic::encryptRow(DB::Row &row) { try { DB::Row crow = row; @@ -154,17 +175,13 @@ void CryptoLogic::encryptRow(const Password &password, DB::Row &row) crow.tag = dataPair.second; - if (!password.empty()) { - key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE); - - crow.data = Crypto::SW::Internals::encryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv); - crow.encryptionScheme |= ENCR_PASSWORD; - } - encBase64(crow.data); crow.encryptionScheme |= ENCR_BASE64; encBase64(crow.iv); + crow.encryptionScheme &= ENCR_ORDER_CLEAR; + crow.encryptionScheme |= ENCR_ORDER_V2; + row = std::move(crow); } catch(const CKM::Base64Encoder::Exception::Base &e) { ThrowErr(Exc::InternalError, e.GetMessage()); @@ -173,6 +190,11 @@ void CryptoLogic::encryptRow(const Password &password, DB::Row &row) } } +int CryptoLogic::getSchemeVersion(int encryptionScheme) +{ + return encryptionScheme >> ENCR_ORDER_OFFSET; +} + void CryptoLogic::decryptRow(const Password &password, DB::Row &row) { try { @@ -200,16 +222,22 @@ void CryptoLogic::decryptRow(const Password &password, DB::Row &row) decBase64(crow.data); } - if (crow.encryptionScheme & ENCR_PASSWORD) { - key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE); - crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv); + if((crow.encryptionScheme >> ENCR_ORDER_OFFSET) == ENCR_ORDER_V2) { + if (crow.encryptionScheme & ENCR_APPKEY) { + key = m_keyMap[crow.ownerLabel]; + crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag); + } + } else { + if (crow.encryptionScheme & ENCR_PASSWORD) { + key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE); + crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv); + } + + if (crow.encryptionScheme & ENCR_APPKEY) { + key = m_keyMap[crow.ownerLabel]; + crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag); + } } - - if (crow.encryptionScheme & ENCR_APPKEY) { - key = m_keyMap[crow.ownerLabel]; - crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag); - } - if (static_cast(crow.data.size()) < crow.dataSize) { ThrowErr(Exc::AuthenticationFailed, "Decrypted row size mismatch"); } diff --git a/src/manager/service/crypto-logic.h b/src/manager/service/crypto-logic.h index b734bb4..61c582e 100644 --- a/src/manager/service/crypto-logic.h +++ b/src/manager/service/crypto-logic.h @@ -38,18 +38,38 @@ public: virtual ~CryptoLogic(){} void decryptRow(const Password &password, DB::Row &row); - void encryptRow(const Password &password, DB::Row &row); + void encryptRow(DB::Row &row); + + static int getSchemeVersion(int encryptionScheme); bool haveKey(const Label &smackLabel); void pushKey(const Label &smackLabel, const RawBuffer &applicationKey); void removeKey(const Label &smackLabel); + static const int ENCRYPTION_V1 = 0; + static const int ENCRYPTION_V2 = 1; + private: + // Encryption scheme flags (enable/disable specific encryption type, multiple choice) static const int ENCR_BASE64 = 1 << 0; static const int ENCR_APPKEY = 1 << 1; static const int ENCR_PASSWORD = 1 << 2; + // Encryption order flags (single choice) + static const int ENCR_ORDER_CLEAR = 0x00ffffff; + static const int ENCR_ORDER_FILTER = ~ENCR_ORDER_CLEAR; + /* + * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and + * optionally by custom user password. Is such form it is stored in db. + */ + static const int ENCR_ORDER_V1 = ENCR_ORDER_CLEAR + 0; + /* + * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with + * user password. Returned token is encrypted with app key and stored in db. + */ + static const int ENCR_ORDER_V2 = ENCR_ORDER_CLEAR + 1; + std::map m_keyMap; RawBuffer generateRandIV() const; diff --git a/src/manager/service/db-crypto.cpp b/src/manager/service/db-crypto.cpp index 8a5b57b..99c0155 100644 --- a/src/manager/service/db-crypto.cpp +++ b/src/manager/service/db-crypto.cpp @@ -94,6 +94,17 @@ namespace { " ?009" " );"; + const char *DB_CMD_OBJECT_UPDATE = + "UPDATE OR FAIL OBJECTS SET" + " algorithmType = ?003," + " encryptionScheme = ?004," + " iv = ?005," + " dataSize = ?006," + " data = ?007," + " tag = ?008" + " WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102)" + " AND dataType = ?002;"; + const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL = "SELECT * FROM [join_name_object_tables] " " WHERE (dataType BETWEEN ?001 AND ?002) " @@ -388,6 +399,21 @@ namespace DB { "Couldn't save Row"); } + void Crypto::updateRow(const Row &row) { + Try { + // transaction is present in the layer above + ObjectTable objectTable(this->m_connection); + objectTable.updateRow(row); + return; + } Catch(SqlConnection::Exception::SyntaxError) { + LogError("Couldn't prepare update statement"); + } Catch(SqlConnection::Exception::InternalError) { + LogError("Couldn't execute update statement"); + } + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't update Row"); + } + bool Crypto::deleteRow( const Name &name, const Label &ownerLabel) @@ -826,6 +852,25 @@ namespace DB { insertObjectCommand->Step(); } + + void Crypto::ObjectTable::updateRow(const Row &row) + { + SqlConnection::DataCommandUniquePtr updateObjectCommand = + m_connection->PrepareDataCommand(DB_CMD_OBJECT_UPDATE); + updateObjectCommand->BindInteger(2, static_cast(row.dataType)); + updateObjectCommand->BindInteger(3, static_cast(row.algorithmType)); + updateObjectCommand->BindInteger(4, row.encryptionScheme); + updateObjectCommand->BindBlob (5, row.iv); + updateObjectCommand->BindInteger(6, row.dataSize); + updateObjectCommand->BindBlob (7, row.data); + updateObjectCommand->BindBlob (8, row.tag); + + // name table reference + updateObjectCommand->BindString (101, row.name.c_str()); + updateObjectCommand->BindString (102, row.ownerLabel.c_str()); + + updateObjectCommand->Step(); + } } // namespace DB } // namespace CKM diff --git a/src/manager/service/db-crypto.h b/src/manager/service/db-crypto.h index c83b1e1..dae0031 100644 --- a/src/manager/service/db-crypto.h +++ b/src/manager/service/db-crypto.h @@ -72,6 +72,9 @@ namespace DB { const Label &owner, const RowVector &rows); + void updateRow( + const Row &row); + bool isNameLabelPresent( const Name &name, const Label &owner) const; @@ -277,6 +280,8 @@ namespace DB { void addRow( const Row &row); + void updateRow( + const Row &row); private: SqlConnection* m_connection; -- 2.7.4 From f8575d6b7807c953fa4693f1588535ce35d88548 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Thu, 1 Oct 2015 08:32:54 +0200 Subject: [PATCH 10/16] Return error if password is not empty and row is not password protected [Problem] If old scheme row is not password protected and the user tries to read it with non empty password it will get reencrypted with this password. [Solution] Throw an authentication exception if password is not empty and row is not password protected. [Verification] Run ckm-tests-internal -t ENCRYPTION_SCHEME_TEST/T120_Read_wrong_pass Change-Id: I44b270dbbefd043b6efb9371f0d7a81c1b234b31 --- src/manager/service/crypto-logic.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/manager/service/crypto-logic.cpp b/src/manager/service/crypto-logic.cpp index c7b8786..6fe6e4e 100644 --- a/src/manager/service/crypto-logic.cpp +++ b/src/manager/service/crypto-logic.cpp @@ -208,13 +208,19 @@ void CryptoLogic::decryptRow(const Password &password, DB::Row &row) if ((row.encryptionScheme & ENCR_PASSWORD) && password.empty()) { ThrowErr(Exc::AuthenticationFailed, - "DB row is password protected, but given password is " - "empty."); + "DB row is password protected, but given password is empty."); + } + + if(!(row.encryptionScheme & ENCR_PASSWORD) && !password.empty()) { + ThrowErr(Exc::AuthenticationFailed, + "DB row is not password protected, but given password is not empty."); } if ((row.encryptionScheme & ENCR_APPKEY) && !haveKey(row.ownerLabel)) { - ThrowErr(Exc::AuthenticationFailed, "Missing application key for ", - row.ownerLabel, " label."); + ThrowErr(Exc::AuthenticationFailed, + "Missing application key for ", + row.ownerLabel, + " label."); } decBase64(crow.iv); -- 2.7.4 From cbb7712357af6b734a876b6d66e72ab1efb294ee Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Tue, 29 Sep 2015 09:07:21 +0200 Subject: [PATCH 11/16] Use exportable=true when reading certificate from db [Problem] Key manager allows creating a cert chain from not exportable certificates. [Solution] CKMLogic::readCertificateHelper modified to use exportable flag equal to 'true'. [Verification] Run ckm-tests-internal -t ENCRYPTION_SCHEME_TEST Change-Id: Ib13811282eb9d1267c26741a578d8c2111bdecbb --- src/manager/service/ckm-logic.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/manager/service/ckm-logic.cpp b/src/manager/service/ckm-logic.cpp index 8736fd5..0e33727 100644 --- a/src/manager/service/ckm-logic.cpp +++ b/src/manager/service/ckm-logic.cpp @@ -1383,7 +1383,14 @@ int CKMLogic::readCertificateHelper( for (auto &i: labelNameVector) { // certificates can't be protected with custom user password Crypto::GObjUPtr obj; - int ec = readDataHelper(false, cred, DataType::CERTIFICATE, i.second, i.first, Password(), obj); + int ec; + ec = readDataHelper(true, + cred, + DataType::CERTIFICATE, + i.second, + i.first, + Password(), + obj); if (ec != CKM_API_SUCCESS) return ec; @@ -1391,7 +1398,13 @@ int CKMLogic::readCertificateHelper( // try to read chain certificates (if present) Crypto::GObjUPtrVector caChainObjs; - ec = readDataHelper(false, cred, DataType::DB_CHAIN_FIRST, i.second, i.first, CKM::Password(), caChainObjs); + ec = readDataHelper(true, + cred, + DataType::DB_CHAIN_FIRST, + i.second, + i.first, + CKM::Password(), + caChainObjs); if(ec != CKM_API_SUCCESS && ec != CKM_API_ERROR_DB_ALIAS_UNKNOWN) return ec; for(auto &caCertObj : caChainObjs) -- 2.7.4 From d31fd54fd0a5edc0b1049ab4394a9dedbf13b72e Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Thu, 24 Sep 2015 11:21:03 +0200 Subject: [PATCH 12/16] Add encryption scheme tests [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 | 3 + tests/encryption-scheme/CMakeLists.txt | 32 ++- tests/encryption-scheme/scheme-test.cpp | 406 +++++++++++++++++++++++++++++++- tests/encryption-scheme/scheme-test.h | 65 ++++- tests/test_encryption-scheme.cpp | 152 ++++++++++++ 5 files changed, 644 insertions(+), 14 deletions(-) create mode 100644 tests/test_encryption-scheme.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 62fea85..8cadd63 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 ) diff --git a/tests/encryption-scheme/CMakeLists.txt b/tests/encryption-scheme/CMakeLists.txt index 8503859..10cea93 100644 --- a/tests/encryption-scheme/CMakeLists.txt +++ b/tests/encryption-scheme/CMakeLists.txt @@ -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}) diff --git a/tests/encryption-scheme/scheme-test.cpp b/tests/encryption-scheme/scheme-test.cpp index 5422bd2..102bb69 100644 --- a/tests/encryption-scheme/scheme-test.cpp +++ b/tests/encryption-scheme/scheme-test.cpp @@ -22,13 +22,25 @@ #include #include +#include +#include +#include #include +#include #include #include +#include + #include +#include +#include +#include +#include +#include + 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 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(output), random_bytes); + if(static_cast(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()); +} diff --git a/tests/encryption-scheme/scheme-test.h b/tests/encryption-scheme/scheme-test.h index 69edd24..6d020b7 100644 --- a/tests/encryption-scheme/scheme-test.h +++ b/tests/encryption-scheme/scheme-test.h @@ -21,15 +21,23 @@ #pragma once +#include #include +#include #include #include #include +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 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 m_db; + bool m_directAccessEnabled; }; diff --git a/tests/test_encryption-scheme.cpp b/tests/test_encryption-scheme.cpp new file mode 100644 index 0000000..9e4579a --- /dev/null +++ b/tests/test_encryption-scheme.cpp @@ -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 +#include + +#include + +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() -- 2.7.4 From c1fe1e83ca540a1290f73dc077a0f8dcc0e67360 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Mon, 26 Oct 2015 13:10:50 +0100 Subject: [PATCH 13/16] Protocol refactoring. Introduce CryptoAlgorithm in internal socket protocol. Change-Id: I70000a05e0a47d2b12af9b11324adf67da0f5e22 --- .../client-async/client-manager-async-impl.cpp | 12 ++++-------- .../client-async/client-manager-async-impl.h | 6 ++---- src/manager/client-async/client-manager-async.cpp | 10 ++++++++-- src/manager/client/client-manager-impl.cpp | 12 ++++-------- src/manager/client/client-manager-impl.h | 6 ++---- src/manager/client/client-manager.cpp | 12 ++++++++---- src/manager/service/ckm-logic.cpp | 13 ++----------- src/manager/service/ckm-logic.h | 6 ++---- src/manager/service/ckm-service.cpp | 21 ++++++++++----------- 9 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/manager/client-async/client-manager-async-impl.cpp b/src/manager/client-async/client-manager-async-impl.cpp index fb7bc8a..269ef13 100644 --- a/src/manager/client-async/client-manager-async-impl.cpp +++ b/src/manager/client-async/client-manager-async-impl.cpp @@ -187,8 +187,7 @@ void ManagerAsync::Impl::createSignature(const ObserverPtr& observer, const Alias& privateKeyAlias, const Password& password, const RawBuffer& message, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding) + const CryptoAlgorithm &cAlg) { observerCheck(observer); if (privateKeyAlias.empty() || message.empty()) { @@ -204,8 +203,7 @@ void ManagerAsync::Impl::createSignature(const ObserverPtr& observer, helper.getLabel(), password, message, - static_cast(hash), - static_cast(padding)); + CryptoAlgorithmSerializable(cAlg)); }, [&observer](int error) {observer->ReceivedError(error);}); } @@ -214,8 +212,7 @@ void ManagerAsync::Impl::verifySignature(const ObserverPtr& observer, const Password& password, const RawBuffer& message, const RawBuffer& signature, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding) + const CryptoAlgorithm &cAlg) { observerCheck(observer); if (publicKeyOrCertAlias.empty() || message.empty() || signature.empty()) { @@ -232,8 +229,7 @@ void ManagerAsync::Impl::verifySignature(const ObserverPtr& observer, password, message, signature, - static_cast(hash), - static_cast(padding)); + CryptoAlgorithmSerializable(cAlg)); }, [&observer](int error){ observer->ReceivedError(error); } ); } diff --git a/src/manager/client-async/client-manager-async-impl.h b/src/manager/client-async/client-manager-async-impl.h index 02c132d..21013fc 100644 --- a/src/manager/client-async/client-manager-async-impl.h +++ b/src/manager/client-async/client-manager-async-impl.h @@ -65,16 +65,14 @@ public: const Alias& privateKeyAlias, const Password& password, const RawBuffer& message, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding); + const CryptoAlgorithm& cAlgorithm); void verifySignature( const ObserverPtr& observer, const Alias& publicKeyOrCertAlias, const Password& password, const RawBuffer& message, const RawBuffer& signature, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding); + const CryptoAlgorithm& cAlgorithm); void ocspCheck( const ObserverPtr& observer, diff --git a/src/manager/client-async/client-manager-async.cpp b/src/manager/client-async/client-manager-async.cpp index f79d12b..92eb207 100644 --- a/src/manager/client-async/client-manager-async.cpp +++ b/src/manager/client-async/client-manager-async.cpp @@ -230,7 +230,10 @@ void ManagerAsync::createSignature(const ObserverPtr& observer, const HashAlgorithm hash, const RSAPaddingAlgorithm padding) { - m_impl->createSignature(observer, privateKeyAlias, password, message, hash, padding); + CryptoAlgorithm cAlg; + cAlg.setParam(ParamName::SV_HASH_ALGO, hash); + cAlg.setParam(ParamName::SV_RSA_PADDING, padding); + m_impl->createSignature(observer, privateKeyAlias, password, message, cAlg); } void ManagerAsync::verifySignature(const ObserverPtr& observer, @@ -241,7 +244,10 @@ void ManagerAsync::verifySignature(const ObserverPtr& observer, const HashAlgorithm hash, const RSAPaddingAlgorithm padding) { - m_impl->verifySignature(observer, publicKeyOrCertAlias, password, message, signature, hash, padding); + CryptoAlgorithm cAlg; + cAlg.setParam(ParamName::SV_HASH_ALGO, hash); + cAlg.setParam(ParamName::SV_RSA_PADDING, padding); + m_impl->verifySignature(observer, publicKeyOrCertAlias, password, message, signature, cAlg); } void ManagerAsync::ocspCheck(const ObserverPtr& observer, diff --git a/src/manager/client/client-manager-impl.cpp b/src/manager/client/client-manager-impl.cpp index da199d7..790e541 100644 --- a/src/manager/client/client-manager-impl.cpp +++ b/src/manager/client/client-manager-impl.cpp @@ -631,8 +631,7 @@ int Manager::Impl::createSignature( const Alias &privateKeyAlias, const Password &password, // password for private_key const RawBuffer &message, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding, + const CryptoAlgorithm &cAlgorithm, RawBuffer &signature) { int my_counter = ++m_counter; @@ -647,8 +646,7 @@ int Manager::Impl::createSignature( helper.getLabel(), password, message, - static_cast(hash), - static_cast(padding)); + CryptoAlgorithmSerializable(cAlgorithm)); int retCode = m_storageConnection.processRequest(send.Pop(), recv); if (CKM_API_SUCCESS != retCode) @@ -673,8 +671,7 @@ int Manager::Impl::verifySignature( const Password &password, // password for public_key (optional) const RawBuffer &message, const RawBuffer &signature, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding) + const CryptoAlgorithm &cAlg) { int my_counter = ++m_counter; @@ -688,8 +685,7 @@ int Manager::Impl::verifySignature( password, message, signature, - static_cast(hash), - static_cast(padding)); + CryptoAlgorithmSerializable(cAlg)); int retCode = m_storageConnection.processRequest(send.Pop(), recv); if (CKM_API_SUCCESS != retCode) diff --git a/src/manager/client/client-manager-impl.h b/src/manager/client/client-manager-impl.h index eebb7fd..29d381d 100644 --- a/src/manager/client/client-manager-impl.h +++ b/src/manager/client/client-manager-impl.h @@ -99,8 +99,7 @@ public: const Alias &privateKeyAlias, const Password &password, // password for private_key const RawBuffer &message, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding, + const CryptoAlgorithm &cAlgorithm, RawBuffer &signature); int verifySignature( @@ -108,8 +107,7 @@ public: const Password &password, // password for public_key (optional) const RawBuffer &message, const RawBuffer &signature, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding); + const CryptoAlgorithm &cAlgorithm); int ocspCheck(const CertificateShPtrVector &certificateChain, int &ocspCheck); diff --git a/src/manager/client/client-manager.cpp b/src/manager/client/client-manager.cpp index 14927e7..6d8ed4b 100644 --- a/src/manager/client/client-manager.cpp +++ b/src/manager/client/client-manager.cpp @@ -174,12 +174,14 @@ int Manager::createSignature( const RSAPaddingAlgorithm padding, RawBuffer &signature) { + CryptoAlgorithm cAlg; + cAlg.setParam(ParamName::SV_HASH_ALGO, hash); + cAlg.setParam(ParamName::SV_RSA_PADDING, padding); return m_impl->createSignature( privateKeyAlias, password, message, - hash, - padding, + cAlg, signature); } @@ -191,13 +193,15 @@ int Manager::verifySignature( const HashAlgorithm hash, const RSAPaddingAlgorithm padding) { + CryptoAlgorithm cAlg; + cAlg.setParam(ParamName::SV_HASH_ALGO, hash); + cAlg.setParam(ParamName::SV_RSA_PADDING, padding); return m_impl->verifySignature( publicKeyOrCertAlias, password, message, signature, - hash, - padding); + cAlg); } int Manager::ocspCheck(const CertificateShPtrVector &certificateChainVector, int &ocspStatus) { diff --git a/src/manager/service/ckm-logic.cpp b/src/manager/service/ckm-logic.cpp index 0e33727..2dc20a7 100644 --- a/src/manager/service/ckm-logic.cpp +++ b/src/manager/service/ckm-logic.cpp @@ -1574,14 +1574,10 @@ RawBuffer CKMLogic::createSignature( const Label & ownerLabel, const Password &password, // password for private_key const RawBuffer &message, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding) + const CryptoAlgorithm &cryptoAlg) { DB::Row row; RawBuffer signature; - CryptoAlgorithm cryptoAlg; - cryptoAlg.setParam(ParamName::SV_HASH_ALGO, hash); - cryptoAlg.setParam(ParamName::SV_RSA_PADDING, padding); int retCode = CKM_API_SUCCESS; @@ -1616,18 +1612,13 @@ RawBuffer CKMLogic::verifySignature( const Password &password, // password for public_key (optional) const RawBuffer &message, const RawBuffer &signature, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding) + const CryptoAlgorithm ¶ms) { int retCode = CKM_API_ERROR_VERIFICATION_FAILED; try { DB::Row row; - CryptoAlgorithm params; - params.setParam(ParamName::SV_HASH_ALGO, hash); - params.setParam(ParamName::SV_RSA_PADDING, padding); - // try certificate first - looking for a public key. // in case of PKCS, pub key from certificate will be found first // rather than private key from the same PKCS. diff --git a/src/manager/service/ckm-logic.h b/src/manager/service/ckm-logic.h index d3f0c40..472fea2 100644 --- a/src/manager/service/ckm-logic.h +++ b/src/manager/service/ckm-logic.h @@ -162,8 +162,7 @@ public: const Label & ownerLabel, const Password &password, // password for private_key const RawBuffer &message, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding); + const CryptoAlgorithm &cryptoAlgorithm); RawBuffer verifySignature( const Credentials &cred, @@ -173,8 +172,7 @@ public: const Password &password, // password for public_key (optional) const RawBuffer &message, const RawBuffer &signature, - const HashAlgorithm hash, - const RSAPaddingAlgorithm padding); + const CryptoAlgorithm &cryptoAlgorithm); RawBuffer updateCCMode(); diff --git a/src/manager/service/ckm-service.cpp b/src/manager/service/ckm-service.cpp index 6a744bd..47fef2b 100644 --- a/src/manager/service/ckm-service.cpp +++ b/src/manager/service/ckm-service.cpp @@ -338,8 +338,10 @@ RawBuffer CKMService::ProcessStorage(Credentials &cred, MessageBuffer &buffer) { Password password; // password for private_key RawBuffer message; - int padding = 0, hash = 0; - buffer.Deserialize(name, label, password, message, hash, padding); + + CryptoAlgorithmSerializable cAlgorithm; + buffer.Deserialize(name, label, password, message, cAlgorithm); + return m_logic->createSignature( cred, msgID, @@ -347,24 +349,22 @@ RawBuffer CKMService::ProcessStorage(Credentials &cred, MessageBuffer &buffer) label, password, // password for private_key message, - static_cast(hash), - static_cast(padding)); + cAlgorithm); } case LogicCommand::VERIFY_SIGNATURE: { Password password; // password for public_key (optional) RawBuffer message; RawBuffer signature; - //HashAlgorithm hash; - //RSAPaddingAlgorithm padding; - int padding = 0, hash = 0; + CryptoAlgorithmSerializable cAlg; + buffer.Deserialize(name, label, password, message, signature, - hash, - padding); + cAlg); + return m_logic->verifySignature( cred, msgID, @@ -373,8 +373,7 @@ RawBuffer CKMService::ProcessStorage(Credentials &cred, MessageBuffer &buffer) password, // password for public_key (optional) message, signature, - static_cast(hash), - static_cast(padding)); + cAlg); } case LogicCommand::SET_PERMISSION: { -- 2.7.4 From 7372be701ca36747cea699c6d2ecac3524a2cffa Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Tue, 10 Nov 2015 11:53:18 +0900 Subject: [PATCH 14/16] Remove MDFPP related code Change-Id: I4b2078f2f2ebc8ebbd31fb3b7995eb1807fc3a49 Signed-off-by: Kyungwook Tak --- CMakeLists.txt | 7 --- packaging/key-manager.spec | 6 +-- src/CMakeLists.txt | 1 - src/listener/CMakeLists.txt | 2 - src/listener/listener-daemon.cpp | 97 +++++++++------------------------- src/manager/service/access-control.cpp | 54 +++++-------------- tools/ckm_db_tool/CMakeLists.txt | 1 - 7 files changed, 38 insertions(+), 130 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab1548c..73720b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,13 +58,6 @@ IF (CMAKE_BUILD_TYPE MATCHES "DEBUG") ADD_DEFINITIONS("-DBUILD_TYPE_DEBUG") ENDIF (CMAKE_BUILD_TYPE MATCHES "DEBUG") -IF (DEFINED SECURITY_MDFPP_STATE_ENABLE) - MESSAGE("SECURITY_MDFPP_STATE_ENABLE ENABLED !") - ADD_DEFINITIONS("-DSECURITY_MDFPP_STATE_ENABLE") -ELSE (DEFINED SECURITY_MDFPP_STATE_ENABLE) - MESSAGE("SECURITY_MDFPP_STATE_ENABLE DISABLED !") -ENDIF (DEFINED SECURITY_MDFPP_STATE_ENABLE) - IF (DEFINED SYSTEMD_ENV_FILE) ADD_DEFINITIONS(-DSYSTEMD_ENV_FILE="${SYSTEMD_ENV_FILE}") ENDIF (DEFINED SYSTEMD_ENV_FILE) diff --git a/packaging/key-manager.spec b/packaging/key-manager.spec index a054ff6..23b89b0 100644 --- a/packaging/key-manager.spec +++ b/packaging/key-manager.spec @@ -17,7 +17,6 @@ BuildRequires: pkgconfig(openssl) BuildRequires: libattr-devel BuildRequires: pkgconfig(libsmack) BuildRequires: pkgconfig(libsystemd-daemon) -BuildRequires: pkgconfig(vconf) BuildRequires: pkgconfig(libsystemd-journal) BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(capi-system-info) @@ -36,8 +35,8 @@ application to sign and verify (DSA/RSA/ECDSA) signatures. %package -n key-manager-listener Summary: Package with listener daemon Group: System/Security -BuildRequires: pkgconfig(vconf) BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(capi-appfw-package-manager) Requires: libkey-manager-client = %{version}-%{release} @@ -120,9 +119,6 @@ export LDFLAGS+="-Wl,--rpath=%{_libdir},-Bsymbolic-functions " %cmake . -DVERSION=%{version} \ -DCMAKE_BUILD_TYPE=%{?build_type:%build_type}%{!?build_type:RELEASE} \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -%if "%{sec_product_feature_security_mdfpp_enable}" == "1" - -DSECURITY_MDFPP_STATE_ENABLE=1 \ -%endif -DSYSTEMD_UNIT_DIR=%{_unitdir} \ -DSYSTEMD_ENV_FILE="/etc/sysconfig/central-key-manager" \ -DMOCKUP_SM=%{?mockup_sm:%mockup_sm}%{!?mockup_sm:OFF} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 131e6d4..aa72fb7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,6 @@ PKG_CHECK_MODULES(KEY_MANAGER_DEP libsystemd-daemon capi-base-common capi-system-info - vconf libxml-2.0 security-manager cynara-client-async diff --git a/src/listener/CMakeLists.txt b/src/listener/CMakeLists.txt index 25e92eb..1518c42 100644 --- a/src/listener/CMakeLists.txt +++ b/src/listener/CMakeLists.txt @@ -3,8 +3,6 @@ PKG_CHECK_MODULES(LISTENER_DEP dlog glib-2.0 capi-appfw-package-manager - libsystemd-daemon - vconf ) SET(LISTENER_SOURCES ${PROJECT_SOURCE_DIR}/src/listener/listener-daemon.cpp) diff --git a/src/listener/listener-daemon.cpp b/src/listener/listener-daemon.cpp index 0568c77..4521bbd 100644 --- a/src/listener/listener-daemon.cpp +++ b/src/listener/listener-daemon.cpp @@ -29,15 +29,10 @@ #include #include -#ifdef SECURITY_MDFPP_STATE_ENABLE -#include -#endif - -#define CKM_LISTENER_TAG "CKM_LISTENER" - -#if defined(SECURITY_MDFPP_STATE_ENABLE) && !defined(VCONFKEY_SECURITY_MDPP_STATE) -#define VCONFKEY_SECURITY_MDPP_STATE "file/security_mdpp/security_mdpp_state" +#ifdef LOG_TAG +#undef LOG_TAG #endif +#define LOG_TAG "CKM_LISTENER" namespace { const char* const CKM_LOCK = "/var/run/key-manager.pid"; @@ -56,30 +51,6 @@ bool isCkmRunning() return (0 != ret); } -#ifdef SECURITY_MDFPP_STATE_ENABLE -void callUpdateCCMode() -{ - if(!isCkmRunning()) - return; - - auto control = CKM::Control::create(); - int ret = control->updateCCMode(); - - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "Callback caller process id : %d\n", getpid()); - - if ( ret != CKM_API_SUCCESS ) - SLOG(LOG_ERROR, CKM_LISTENER_TAG, "CKM::Control::updateCCMode error. ret : %d\n", ret); - else - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "CKM::Control::updateCCMode success.\n"); -} - -void ccModeChangedEventCallback(keynode_t*, void*) -{ - callUpdateCCMode(); -} -#endif - - void packageUninstalledEventCallback( const char *type, const char *package, @@ -96,59 +67,41 @@ void packageUninstalledEventCallback( if (eventType != PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL || eventState != PACKAGE_MANAGER_EVENT_STATE_STARTED || - package == NULL) { - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "PackageUninstalled Callback error of Invalid Param"); - } - else { - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "PackageUninstalled Callback. Uninstalation of: %s", package); - auto control = CKM::Control::create(); - int ret = 0; - if ( CKM_API_SUCCESS != (ret = control->removeApplicationData(std::string(package))) ) { - SLOG(LOG_ERROR, CKM_LISTENER_TAG, "CKM::Control::removeApplicationData error. ret : %d\n", ret); - } - else { - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, - "CKM::Control::removeApplicationData success. Uninstallation package : %s\n", package); - } + package == NULL) + return; + + SLOGD("PackageUninstalled Callback. Uninstalation of: %s", package); + + if (!isCkmRunning()) { + SLOGE("package uninstall event recieved but ckm isn't running!"); + return; } + + auto control = CKM::Control::create(); + int ret = control->removeApplicationData(std::string(package)); + if (ret != CKM_API_SUCCESS) + SLOGE("CKM::Control::removeApplicationData error. ret : %d", ret); + else + SLOGD("CKM::Control::removeApplicationData success. Uninstallation package : %s", package); } -int main(void) { - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "%s", "Start!"); +int main(void) +{ + SLOGD("Start!"); - // Let's start to listen GMainLoop *main_loop = g_main_loop_new(NULL, FALSE); package_manager_h request; package_manager_create(&request); - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "register uninstalledApp event callback start"); + SLOGD("register uninstalledApp event callback start"); if (0 != package_manager_set_event_cb(request, packageUninstalledEventCallback, NULL)) { - SLOG(LOG_ERROR, CKM_LISTENER_TAG, "%s", "Error in package_manager_set_event_cb"); + SLOGE("Error in package_manager_set_event_cb"); exit(-1); } - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "register uninstalledApp event callback success"); - -#ifdef SECURITY_MDFPP_STATE_ENABLE - int ret = 0; - char *mdpp_state = vconf_get_str(VCONFKEY_SECURITY_MDPP_STATE); - if ( mdpp_state ) { // Update cc mode and register event callback only when mdpp vconf key exists - callUpdateCCMode(); - - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "register vconfCCModeChanged event callback start"); - if ( 0 != (ret = vconf_notify_key_changed(VCONFKEY_SECURITY_MDPP_STATE, ccModeChangedEventCallback, NULL)) ) { - SLOG(LOG_ERROR, CKM_LISTENER_TAG, "Error in vconf_notify_key_changed. ret : %d", ret); - exit(-1); - } - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "register vconfCCModeChanged event callback success"); - } - else - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, - "vconfCCModeChanged event callback is not registered. No vconf key exists : %s", VCONFKEY_SECURITY_MDPP_STATE); -#endif - - SLOG(LOG_DEBUG, CKM_LISTENER_TAG, "%s", "Ready to listen!"); + SLOGD("Ready to listen!"); g_main_loop_run(main_loop); + return 0; } diff --git a/src/manager/service/access-control.cpp b/src/manager/service/access-control.cpp index decd92c..e5eba2b 100644 --- a/src/manager/service/access-control.cpp +++ b/src/manager/service/access-control.cpp @@ -25,58 +25,28 @@ #include #include -#ifdef SECURITY_MDFPP_STATE_ENABLE -#include -#endif - -#if defined(SECURITY_MDFPP_STATE_ENABLE) && !defined(VCONFKEY_SECURITY_MDPP_STATE) -#define VCONFKEY_SECURITY_MDPP_STATE "file/security_mdpp/security_mdpp_state" -#endif - namespace { -const char* const MDPP_MODE_ENFORCING = "Enforcing"; -const char* const MDPP_MODE_ENABLED = "Enabled"; -const char* const MDPP_MODE_DISABLED = "Disabled"; -const uid_t SYSTEM_SVC_MAX_UID = (5000 - 1); +const uid_t SYSTEM_SVC_MAX_UID = (5000 - 1); } // anonymous namespace namespace CKM { -void AccessControl::updateCCMode() { - int fipsModeStatus = 0; - int rc = 0; - bool newMode; - -#ifdef SECURITY_MDFPP_STATE_ENABLE - char *mdppState = vconf_get_str(VCONFKEY_SECURITY_MDPP_STATE); -#else - char *mdppState = NULL; -#endif - newMode = ( mdppState && (!strcmp(mdppState, MDPP_MODE_ENABLED) || - !strcmp(mdppState, MDPP_MODE_ENFORCING) || - !strcmp(mdppState, MDPP_MODE_DISABLED))); +void AccessControl::updateCCMode() +{ + /* newMode should be extracted from global property like buxton in product */ + bool newMode = false; + if (newMode == m_ccMode) return; - m_ccMode = newMode; + int iNewMode = newMode ? 1 : 0; - fipsModeStatus = FIPS_mode(); - - if(m_ccMode) { - if(fipsModeStatus == 0) { // If FIPS mode off - rc = FIPS_mode_set(1); // Change FIPS_mode from off to on - if(rc == 0) { - LogError("Error in FIPS_mode_set function"); - } - } - } else { - if(fipsModeStatus == 1) { // If FIPS mode on - rc = FIPS_mode_set(0); // Change FIPS_mode from on to off - if(rc == 0) { - LogError("Error in FIPS_mode_set function"); - } - } + if (FIPS_mode_set(iNewMode) == 0) { + LogError("Error to FIPS_mode_set with param " << iNewMode); + return; } + + m_ccMode = newMode; } bool AccessControl::isCCMode() const diff --git a/tools/ckm_db_tool/CMakeLists.txt b/tools/ckm_db_tool/CMakeLists.txt index c8fb53c..8309d5d 100644 --- a/tools/ckm_db_tool/CMakeLists.txt +++ b/tools/ckm_db_tool/CMakeLists.txt @@ -8,7 +8,6 @@ PKG_CHECK_MODULES(CKM_DB_TOOL_DEP libcrypto capi-base-common capi-system-info - vconf libxml-2.0 cynara-client-async cynara-creds-socket -- 2.7.4 From 69d43d7fa2230899f677c88f8d1e1b52071408dc Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Fri, 13 Nov 2015 17:54:10 +0900 Subject: [PATCH 15/16] try-catch enclosed to be exception safe of CAPI Change-Id: I8c88402c6ed8f73bb1e5510389fec2aa07cfd48c Signed-off-by: Kyungwook Tak --- src/manager/client-capi/ckmc-manager.cpp | 890 +++++++++++++++++-------------- src/manager/client/client-manager.cpp | 6 +- 2 files changed, 482 insertions(+), 414 deletions(-) diff --git a/src/manager/client-capi/ckmc-manager.cpp b/src/manager/client-capi/ckmc-manager.cpp index 6565dc9..d668f51 100644 --- a/src/manager/client-capi/ckmc-manager.cpp +++ b/src/manager/client-capi/ckmc-manager.cpp @@ -20,6 +20,7 @@ * @brief provides conversion methods to C from C++ for key-manager control functions. */ +#include #include #include #include @@ -153,41 +154,58 @@ int _cryptoOperation(cryptoFn operation, return ckmc_buffer_new(outBuffer.data(), outBuffer.size(), ppout); } +int try_catch_enclosure(const std::function &func) +{ + try { + return func(); + } catch (const std::bad_alloc &e) { + LogError("memory allocation exception: " << e.what()); + return CKMC_ERROR_OUT_OF_MEMORY; + } catch (const std::exception &e) { + LogError("std exception occured: " << e.what()); + return CKMC_ERROR_UNKNOWN; + } catch (...) { + LogError("Unknown exception occured."); + return CKMC_ERROR_UNKNOWN; + } } +} KEY_MANAGER_CAPI int ckmc_save_key(const char *alias, const ckmc_key_s key, const ckmc_policy_s policy) { - CKM::ManagerShPtr mgr = CKM::Manager::create(); + return try_catch_enclosure([&]()->int { + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if(alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::Alias ckmAlias(alias); - - if(key.raw_key == NULL || key.key_size <= 0) { - return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::RawBuffer buffer(key.raw_key, key.raw_key + key.key_size); + if(alias == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } + CKM::Alias ckmAlias(alias); - CKM::KeyShPtr ckmKey; - if(key.key_type == CKMC_KEY_AES) - { - if(key.password) + if(key.raw_key == NULL || key.key_size <= 0) { return CKMC_ERROR_INVALID_PARAMETER; - ckmKey = CKM::Key::createAES(buffer); - } - else - ckmKey = CKM::Key::create(buffer, _tostring(key.password)); - if(ckmKey.get() == NULL) { - return CKMC_ERROR_INVALID_FORMAT; - } + } + CKM::RawBuffer buffer(key.raw_key, key.raw_key + key.key_size); + + CKM::KeyShPtr ckmKey; + if(key.key_type == CKMC_KEY_AES) + { + if(key.password) + return CKMC_ERROR_INVALID_PARAMETER; + ckmKey = CKM::Key::createAES(buffer); + } + else + ckmKey = CKM::Key::create(buffer, _tostring(key.password)); + if(ckmKey.get() == NULL) { + return CKMC_ERROR_INVALID_FORMAT; + } - CKM::Policy storePolicy(_tostring(policy.password), policy.extractable); + CKM::Policy storePolicy(_tostring(policy.password), policy.extractable); - int ret = mgr->saveKey(ckmAlias, ckmKey, storePolicy); - return to_ckmc_error(ret); + int ret = mgr->saveKey(ckmAlias, ckmKey, storePolicy); + return to_ckmc_error(ret); + }); } @@ -200,337 +218,357 @@ int ckmc_remove_key(const char *alias) KEY_MANAGER_CAPI int ckmc_get_key(const char *alias, const char *password, ckmc_key_s **key) { - int ret; - CKM::KeyShPtr ckmKey; + return try_catch_enclosure([&]()->int { + int ret; + CKM::KeyShPtr ckmKey; - if(alias == NULL || key == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(alias == NULL || key == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::ManagerShPtr mgr = CKM::Manager::create(); - if( (ret = mgr->getKey(alias, _tostring(password), ckmKey)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::ManagerShPtr mgr = CKM::Manager::create(); + if( (ret = mgr->getKey(alias, _tostring(password), ckmKey)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - CKM::RawBuffer buffer = ckmKey->getDER(); - ckmc_key_type_e keyType = static_cast(static_cast(ckmKey->getType())); + CKM::RawBuffer buffer = ckmKey->getDER(); + ckmc_key_type_e keyType = static_cast(static_cast(ckmKey->getType())); - ret = ckmc_key_new( buffer.data(), buffer.size(), keyType, NULL, key); + ret = ckmc_key_new( buffer.data(), buffer.size(), keyType, NULL, key); - return to_ckmc_error(ret); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI int ckmc_get_key_alias_list(ckmc_alias_list_s** alias_list) { - int ret; + return try_catch_enclosure([&]()->int { + int ret; - if (alias_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if (alias_list == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::AliasVector aliasVector; - CKM::ManagerShPtr mgr = CKM::Manager::create(); + CKM::AliasVector aliasVector; + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if ((ret = mgr->getKeyAliasVector(aliasVector)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + if ((ret = mgr->getKeyAliasVector(aliasVector)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - ckmc_alias_list_s *plist = NULL; + ckmc_alias_list_s *plist = NULL; - for (const auto it : aliasVector) { - char *alias = strndup(it.c_str(), it.size()); + for (const auto it : aliasVector) { + char *alias = strndup(it.c_str(), it.size()); - if (plist == NULL) { // first - ret = ckmc_alias_list_new(alias, &plist); - *alias_list = plist; // save the pointer of the first element - } else { - ret = ckmc_alias_list_add(plist, alias, &plist); - } + if (plist == NULL) { // first + ret = ckmc_alias_list_new(alias, &plist); + *alias_list = plist; // save the pointer of the first element + } else { + ret = ckmc_alias_list_add(plist, alias, &plist); + } - if (ret != CKMC_ERROR_NONE) { - free(alias); - ckmc_alias_list_all_free(*alias_list); - return ret; + if (ret != CKMC_ERROR_NONE) { + free(alias); + ckmc_alias_list_all_free(*alias_list); + return ret; + } } - } - if(plist == NULL) { // if the alias_list size is zero - return CKMC_ERROR_DB_ALIAS_UNKNOWN; - } + if(plist == NULL) { // if the alias_list size is zero + return CKMC_ERROR_DB_ALIAS_UNKNOWN; + } - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI int ckmc_save_cert(const char *alias, const ckmc_cert_s cert, const ckmc_policy_s policy) { - if(alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::Alias ckmAlias(alias); - - if(cert.raw_cert == NULL || cert.cert_size <= 0) { + return try_catch_enclosure([&]()->int { + if(alias == NULL) { return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::CertificateShPtr ckmCert = _toCkmCertificate(&cert); - if(ckmCert.get() == NULL) { - return CKMC_ERROR_INVALID_FORMAT; - } + } + CKM::Alias ckmAlias(alias); - CKM::Policy storePolicy(_tostring(policy.password), policy.extractable); + if(cert.raw_cert == NULL || cert.cert_size <= 0) { + return CKMC_ERROR_INVALID_PARAMETER; + } + CKM::CertificateShPtr ckmCert = _toCkmCertificate(&cert); + if(ckmCert.get() == NULL) { + return CKMC_ERROR_INVALID_FORMAT; + } - CKM::ManagerShPtr mgr = CKM::Manager::create(); - int ret = mgr->saveCertificate(ckmAlias, ckmCert, storePolicy); + CKM::Policy storePolicy(_tostring(policy.password), policy.extractable); - return to_ckmc_error(ret); + CKM::ManagerShPtr mgr = CKM::Manager::create(); + int ret = mgr->saveCertificate(ckmAlias, ckmCert, storePolicy); + + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI int ckmc_remove_cert(const char *alias) { - return ckmc_remove_alias(alias); + return ckmc_remove_alias(alias); } KEY_MANAGER_CAPI int ckmc_get_cert(const char *alias, const char *password, ckmc_cert_s **cert) { - CKM::CertificateShPtr ckmCert; - int ret; + return try_catch_enclosure([&]()->int { + CKM::CertificateShPtr ckmCert; + int ret; - if(alias == NULL || cert == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(alias == NULL || cert == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::ManagerShPtr mgr = CKM::Manager::create(); - if( (ret = mgr->getCertificate(alias, _tostring(password), ckmCert)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::ManagerShPtr mgr = CKM::Manager::create(); + if( (ret = mgr->getCertificate(alias, _tostring(password), ckmCert)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - CKM::RawBuffer buffer = ckmCert->getDER(); - ret = ckmc_cert_new( buffer.data(), buffer.size(), CKMC_FORM_DER, cert); + CKM::RawBuffer buffer = ckmCert->getDER(); + ret = ckmc_cert_new( buffer.data(), buffer.size(), CKMC_FORM_DER, cert); - return ret; + return ret; + }); } KEY_MANAGER_CAPI int ckmc_get_cert_alias_list(ckmc_alias_list_s** alias_list) { - int ret; + return try_catch_enclosure([&]()->int { + int ret; - if (alias_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if (alias_list == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - *alias_list = NULL; + *alias_list = NULL; - CKM::AliasVector aliasVector; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - if ((ret = mgr->getCertificateAliasVector(aliasVector)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::AliasVector aliasVector; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + if ((ret = mgr->getCertificateAliasVector(aliasVector)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - ckmc_alias_list_s *plist = NULL; + ckmc_alias_list_s *plist = NULL; - for (const auto it : aliasVector) { - char *alias = strndup(it.c_str(), it.size()); + for (const auto it : aliasVector) { + char *alias = strndup(it.c_str(), it.size()); - if (plist == NULL) { // first - ret = ckmc_alias_list_new(alias, &plist); - *alias_list = plist; // save the pointer of the first element - } else { - ret = ckmc_alias_list_add(plist, alias, &plist); - } + if (plist == NULL) { // first + ret = ckmc_alias_list_new(alias, &plist); + *alias_list = plist; // save the pointer of the first element + } else { + ret = ckmc_alias_list_add(plist, alias, &plist); + } - if (ret != CKMC_ERROR_NONE) { - free(alias); - ckmc_alias_list_all_free(*alias_list); - return ret; + if (ret != CKMC_ERROR_NONE) { + free(alias); + ckmc_alias_list_all_free(*alias_list); + return ret; + } } - } - if(plist == NULL) { // if the alias_list size is zero - return CKMC_ERROR_DB_ALIAS_UNKNOWN; - } + if(plist == NULL) { // if the alias_list size is zero + return CKMC_ERROR_DB_ALIAS_UNKNOWN; + } - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI int ckmc_save_pkcs12(const char *alias, const ckmc_pkcs12_s *ppkcs, const ckmc_policy_s key_policy, const ckmc_policy_s cert_policy) { - CKM::KeyShPtr private_key; - CKM::CertificateShPtr cert; - CKM::CertificateShPtrVector ca_cert_list; + return try_catch_enclosure([&]()->int { + CKM::KeyShPtr private_key; + CKM::CertificateShPtr cert; + CKM::CertificateShPtrVector ca_cert_list; - if(alias==NULL || ppkcs==NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::Alias ckmAlias(alias); - private_key = _toCkmKey(ppkcs->priv_key); - cert = _toCkmCertificate(ppkcs->cert); - ca_cert_list = _toCkmCertificateVector(ppkcs->ca_chain); + if(alias==NULL || ppkcs==NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } + CKM::Alias ckmAlias(alias); + private_key = _toCkmKey(ppkcs->priv_key); + cert = _toCkmCertificate(ppkcs->cert); + ca_cert_list = _toCkmCertificateVector(ppkcs->ca_chain); - CKM::Policy keyPolicy(_tostring(key_policy.password), key_policy.extractable); - CKM::Policy certPolicy(_tostring(cert_policy.password), cert_policy.extractable); + CKM::Policy keyPolicy(_tostring(key_policy.password), key_policy.extractable); + CKM::Policy certPolicy(_tostring(cert_policy.password), cert_policy.extractable); - CKM::PKCS12ShPtr pkcs12(new CKM::PKCS12Impl(private_key, cert, ca_cert_list)); + CKM::PKCS12ShPtr pkcs12(new CKM::PKCS12Impl(private_key, cert, ca_cert_list)); - CKM::ManagerShPtr mgr = CKM::Manager::create(); - int ret = mgr->savePKCS12(ckmAlias, pkcs12, keyPolicy, certPolicy); + CKM::ManagerShPtr mgr = CKM::Manager::create(); + int ret = mgr->savePKCS12(ckmAlias, pkcs12, keyPolicy, certPolicy); - return to_ckmc_error(ret); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI int ckmc_get_pkcs12(const char *alias, const char *key_password, const char *cert_password, ckmc_pkcs12_s **pkcs12) { - int ret; - CKM::PKCS12ShPtr pkcs; - CKM::Password keyPass, certPass; - ckmc_key_s *private_key = NULL; - ckmc_cert_s *cert = NULL; - ckmc_cert_list_s *ca_cert_list = 0; - - if(!alias || !pkcs12) { - return CKMC_ERROR_INVALID_PARAMETER; - } + return try_catch_enclosure([&]()->int { + int ret; + CKM::PKCS12ShPtr pkcs; + CKM::Password keyPass, certPass; + ckmc_key_s *private_key = NULL; + ckmc_cert_s *cert = NULL; + ckmc_cert_list_s *ca_cert_list = 0; - if (key_password) - keyPass = key_password; + if(!alias || !pkcs12) { + return CKMC_ERROR_INVALID_PARAMETER; + } - if (cert_password) - certPass = cert_password; + if (key_password) + keyPass = key_password; - auto mgr = CKM::Manager::create(); + if (cert_password) + certPass = cert_password; - if((ret = mgr->getPKCS12(alias, keyPass, certPass, pkcs)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + auto mgr = CKM::Manager::create(); - if(!pkcs) - return CKMC_ERROR_BAD_RESPONSE; + if((ret = mgr->getPKCS12(alias, keyPass, certPass, pkcs)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - auto pkcsKey = pkcs->getKey(); - if(pkcsKey) - { - CKM::RawBuffer buffer = pkcsKey->getDER(); - ckmc_key_type_e keyType = static_cast(pkcsKey->getType()); - ret = ckmc_key_new(buffer.data(), buffer.size(), keyType, NULL, &private_key); - if(ret != CKMC_ERROR_NONE) - return ret; - } + if(!pkcs) + return CKMC_ERROR_BAD_RESPONSE; + + auto pkcsKey = pkcs->getKey(); + if(pkcsKey) + { + CKM::RawBuffer buffer = pkcsKey->getDER(); + ckmc_key_type_e keyType = static_cast(pkcsKey->getType()); + ret = ckmc_key_new(buffer.data(), buffer.size(), keyType, NULL, &private_key); + if(ret != CKMC_ERROR_NONE) + return ret; + } - auto pkcsCert = pkcs->getCertificate(); - if(pkcsCert) - { - CKM::RawBuffer buffer = pkcsCert->getDER(); - ret = ckmc_cert_new(buffer.data(), buffer.size(), CKMC_FORM_DER, &cert); - if(ret != CKMC_ERROR_NONE) { - ckmc_key_free(private_key); - return ret; + auto pkcsCert = pkcs->getCertificate(); + if(pkcsCert) + { + CKM::RawBuffer buffer = pkcsCert->getDER(); + ret = ckmc_cert_new(buffer.data(), buffer.size(), CKMC_FORM_DER, &cert); + if(ret != CKMC_ERROR_NONE) { + ckmc_key_free(private_key); + return ret; + } } - } - ca_cert_list = _toNewCkmCertList(pkcs->getCaCertificateShPtrVector()); + ca_cert_list = _toNewCkmCertList(pkcs->getCaCertificateShPtrVector()); - ret = ckmc_pkcs12_new(private_key, cert, ca_cert_list, pkcs12); - if(ret != CKMC_ERROR_NONE) - { - ckmc_key_free(private_key); - ckmc_cert_free(cert); - ckmc_cert_list_free(ca_cert_list); - } - return ret; + ret = ckmc_pkcs12_new(private_key, cert, ca_cert_list, pkcs12); + if(ret != CKMC_ERROR_NONE) + { + ckmc_key_free(private_key); + ckmc_cert_free(cert); + ckmc_cert_list_free(ca_cert_list); + } + return ret; + }); } KEY_MANAGER_CAPI int ckmc_save_data(const char *alias, ckmc_raw_buffer_s data, const ckmc_policy_s policy) { - if(alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::Alias ckmAlias(alias); - - if(data.data == NULL || data.size <= 0) { + return try_catch_enclosure([&]()->int { + if(alias == NULL) { return CKMC_ERROR_INVALID_PARAMETER; - } - CKM::RawBuffer buffer(data.data, data.data + data.size); + } + CKM::Alias ckmAlias(alias); - CKM::Policy storePolicy(_tostring(policy.password), policy.extractable); + if(data.data == NULL || data.size <= 0) { + return CKMC_ERROR_INVALID_PARAMETER; + } + CKM::RawBuffer buffer(data.data, data.data + data.size); - CKM::ManagerShPtr mgr = CKM::Manager::create(); - int ret = mgr->saveData(ckmAlias, buffer, storePolicy); + CKM::Policy storePolicy(_tostring(policy.password), policy.extractable); - return to_ckmc_error(ret); + CKM::ManagerShPtr mgr = CKM::Manager::create(); + int ret = mgr->saveData(ckmAlias, buffer, storePolicy); + + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI int ckmc_remove_data(const char *alias) { - return ckmc_remove_alias(alias); + return ckmc_remove_alias(alias); } KEY_MANAGER_CAPI int ckmc_get_data(const char *alias, const char *password, ckmc_raw_buffer_s **data) { - CKM::RawBuffer ckmBuff; - int ret; + return try_catch_enclosure([&]()->int { + CKM::RawBuffer ckmBuff; + int ret; - if(alias == NULL || data == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(alias == NULL || data == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::ManagerShPtr mgr = CKM::Manager::create(); - if( (ret = mgr->getData(alias, _tostring(password), ckmBuff)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::ManagerShPtr mgr = CKM::Manager::create(); + if( (ret = mgr->getData(alias, _tostring(password), ckmBuff)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - ret = ckmc_buffer_new(ckmBuff.data(), ckmBuff.size(), data); + ret = ckmc_buffer_new(ckmBuff.data(), ckmBuff.size(), data); - return ret; + return ret; + }); } KEY_MANAGER_CAPI int ckmc_get_data_alias_list(ckmc_alias_list_s** alias_list){ - int ret; + return try_catch_enclosure([&]()->int { + int ret; - if(alias_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(alias_list == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - *alias_list = NULL; + *alias_list = NULL; - CKM::AliasVector aliasVector; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - if( (ret = mgr->getDataAliasVector(aliasVector)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::AliasVector aliasVector; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + if( (ret = mgr->getDataAliasVector(aliasVector)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - ckmc_alias_list_s *plist = NULL; + ckmc_alias_list_s *plist = NULL; - for (const auto it : aliasVector) { - char *alias = strndup(it.c_str(), it.size()); + for (const auto it : aliasVector) { + char *alias = strndup(it.c_str(), it.size()); - if (plist == NULL) { // first - ret = ckmc_alias_list_new(alias, &plist); - *alias_list = plist; // save the pointer of the first element - } else { - ret = ckmc_alias_list_add(plist, alias, &plist); - } + if (plist == NULL) { // first + ret = ckmc_alias_list_new(alias, &plist); + *alias_list = plist; // save the pointer of the first element + } else { + ret = ckmc_alias_list_add(plist, alias, &plist); + } - if (ret != CKMC_ERROR_NONE) { - free(alias); - ckmc_alias_list_all_free(*alias_list); - return ret; + if (ret != CKMC_ERROR_NONE) { + free(alias); + ckmc_alias_list_all_free(*alias_list); + return ret; + } } - } - if(plist == NULL) { // if the alias_list size is zero - return CKMC_ERROR_DB_ALIAS_UNKNOWN; - } + if(plist == NULL) { // if the alias_list size is zero + return CKMC_ERROR_DB_ALIAS_UNKNOWN; + } - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI @@ -540,20 +578,22 @@ int ckmc_create_key_pair_rsa(const size_t size, const ckmc_policy_s policy_private_key, const ckmc_policy_s policy_public_key) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if(private_key_alias == NULL || public_key_alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(private_key_alias == NULL || public_key_alias == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::Alias ckmPrivakeKeyAlias(private_key_alias); - CKM::Alias ckmPublicKeyAlias(public_key_alias); - CKM::Policy ckmPrivateKeyPolicy(_tostring(policy_private_key.password), policy_private_key.extractable); - CKM::Policy ckmPublicKeyPolicy(_tostring(policy_public_key.password), policy_public_key.extractable); + CKM::Alias ckmPrivakeKeyAlias(private_key_alias); + CKM::Alias ckmPublicKeyAlias(public_key_alias); + CKM::Policy ckmPrivateKeyPolicy(_tostring(policy_private_key.password), policy_private_key.extractable); + CKM::Policy ckmPublicKeyPolicy(_tostring(policy_public_key.password), policy_public_key.extractable); - ret = mgr->createKeyPairRSA(static_cast(size), ckmPrivakeKeyAlias, ckmPublicKeyAlias, ckmPrivateKeyPolicy, ckmPublicKeyPolicy); - return to_ckmc_error(ret); + ret = mgr->createKeyPairRSA(static_cast(size), ckmPrivakeKeyAlias, ckmPublicKeyAlias, ckmPrivateKeyPolicy, ckmPublicKeyPolicy); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI @@ -563,20 +603,22 @@ int ckmc_create_key_pair_dsa(const size_t size, const ckmc_policy_s policy_private_key, const ckmc_policy_s policy_public_key) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if(private_key_alias == NULL || public_key_alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(private_key_alias == NULL || public_key_alias == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::Alias ckmPrivakeKeyAlias(private_key_alias); - CKM::Alias ckmPublicKeyAlias(public_key_alias); - CKM::Policy ckmPrivateKeyPolicy(_tostring(policy_private_key.password), policy_private_key.extractable); - CKM::Policy ckmPublicKeyPolicy(_tostring(policy_public_key.password), policy_public_key.extractable); + CKM::Alias ckmPrivakeKeyAlias(private_key_alias); + CKM::Alias ckmPublicKeyAlias(public_key_alias); + CKM::Policy ckmPrivateKeyPolicy(_tostring(policy_private_key.password), policy_private_key.extractable); + CKM::Policy ckmPublicKeyPolicy(_tostring(policy_public_key.password), policy_public_key.extractable); - ret = mgr->createKeyPairDSA(static_cast(size), ckmPrivakeKeyAlias, ckmPublicKeyAlias, ckmPrivateKeyPolicy, ckmPublicKeyPolicy); - return to_ckmc_error(ret); + ret = mgr->createKeyPairDSA(static_cast(size), ckmPrivakeKeyAlias, ckmPublicKeyAlias, ckmPrivateKeyPolicy, ckmPublicKeyPolicy); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI @@ -586,20 +628,22 @@ int ckmc_create_key_pair_ecdsa(const ckmc_ec_type_e type, const ckmc_policy_s policy_private_key, const ckmc_policy_s policy_public_key) { - CKM::ManagerShPtr mgr = CKM::Manager::create(); + return try_catch_enclosure([&]()->int { + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if(private_key_alias == NULL || public_key_alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(private_key_alias == NULL || public_key_alias == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::ElipticCurve ckmType = static_cast(static_cast(type)); - CKM::Alias ckmPrivakeKeyAlias(private_key_alias); - CKM::Alias ckmPublicKeyAlias(public_key_alias); - CKM::Policy ckmPrivateKeyPolicy(_tostring(policy_private_key.password), policy_private_key.extractable); - CKM::Policy ckmPublicKeyPolicy(_tostring(policy_public_key.password), policy_public_key.extractable); + CKM::ElipticCurve ckmType = static_cast(static_cast(type)); + CKM::Alias ckmPrivakeKeyAlias(private_key_alias); + CKM::Alias ckmPublicKeyAlias(public_key_alias); + CKM::Policy ckmPrivateKeyPolicy(_tostring(policy_private_key.password), policy_private_key.extractable); + CKM::Policy ckmPublicKeyPolicy(_tostring(policy_public_key.password), policy_public_key.extractable); - int ret = mgr->createKeyPairECDSA(ckmType, ckmPrivakeKeyAlias, ckmPublicKeyAlias, ckmPrivateKeyPolicy, ckmPublicKeyPolicy); - return to_ckmc_error(ret); + int ret = mgr->createKeyPairECDSA(ckmType, ckmPrivakeKeyAlias, ckmPublicKeyAlias, ckmPrivateKeyPolicy, ckmPublicKeyPolicy); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI @@ -607,16 +651,18 @@ int ckmc_create_key_aes(size_t size, const char *key_alias, ckmc_policy_s key_policy) { - CKM::ManagerShPtr mgr = CKM::Manager::create(); + return try_catch_enclosure([&]()->int { + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if(key_alias == NULL) - return CKMC_ERROR_INVALID_PARAMETER; + if(key_alias == NULL) + return CKMC_ERROR_INVALID_PARAMETER; - CKM::Alias ckmKeyAlias(key_alias); - CKM::Policy ckmKeyPolicy(_tostring(key_policy.password), key_policy.extractable); + CKM::Alias ckmKeyAlias(key_alias); + CKM::Policy ckmKeyPolicy(_tostring(key_policy.password), key_policy.extractable); - int ret = mgr->createKeyAES(size, ckmKeyAlias, ckmKeyPolicy); - return to_ckmc_error(ret); + int ret = mgr->createKeyAES(size, ckmKeyAlias, ckmKeyPolicy); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI @@ -627,32 +673,34 @@ int ckmc_create_signature(const char *private_key_alias, const ckmc_rsa_padding_algo_e padding, ckmc_raw_buffer_s **signature) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - CKM::RawBuffer ckmSignature; + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + CKM::RawBuffer ckmSignature; - if(private_key_alias == NULL || signature == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(private_key_alias == NULL || signature == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::Alias ckmPrivakeKeyAlias(private_key_alias); - CKM::RawBuffer ckmMessage(message.data, message.data + message.size); - CKM::HashAlgorithm ckmHashAlgo = static_cast(static_cast(hash)); - CKM::RSAPaddingAlgorithm ckmPadding = static_cast(static_cast(padding)); - - if( (ret = mgr->createSignature( - ckmPrivakeKeyAlias, - _tostring(password), - ckmMessage, - ckmHashAlgo, - ckmPadding, - ckmSignature)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::Alias ckmPrivakeKeyAlias(private_key_alias); + CKM::RawBuffer ckmMessage(message.data, message.data + message.size); + CKM::HashAlgorithm ckmHashAlgo = static_cast(static_cast(hash)); + CKM::RSAPaddingAlgorithm ckmPadding = static_cast(static_cast(padding)); + + if( (ret = mgr->createSignature( + ckmPrivakeKeyAlias, + _tostring(password), + ckmMessage, + ckmHashAlgo, + ckmPadding, + ckmSignature)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - ret = ckmc_buffer_new( ckmSignature.data(), ckmSignature.size(), signature); + ret = ckmc_buffer_new( ckmSignature.data(), ckmSignature.size(), signature); - return ret; + return ret; + }); } KEY_MANAGER_CAPI @@ -663,84 +711,90 @@ int ckmc_verify_signature(const char *public_key_alias, const ckmc_hash_algo_e hash, const ckmc_rsa_padding_algo_e padding) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); - if(public_key_alias == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(public_key_alias == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::Alias ckmPublicKeyAlias(public_key_alias); - CKM::RawBuffer ckmMessage(message.data, message.data + message.size); - CKM::RawBuffer ckmSignature(signature.data, signature.data + signature.size); - CKM::HashAlgorithm ckmHashAlgo = static_cast(static_cast(hash)); - CKM::RSAPaddingAlgorithm ckmPadding = static_cast(static_cast(padding)); - - if( (ret = mgr->verifySignature( - ckmPublicKeyAlias, - _tostring(password), - ckmMessage, - ckmSignature, - ckmHashAlgo, - ckmPadding)) != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + CKM::Alias ckmPublicKeyAlias(public_key_alias); + CKM::RawBuffer ckmMessage(message.data, message.data + message.size); + CKM::RawBuffer ckmSignature(signature.data, signature.data + signature.size); + CKM::HashAlgorithm ckmHashAlgo = static_cast(static_cast(hash)); + CKM::RSAPaddingAlgorithm ckmPadding = static_cast(static_cast(padding)); + + if( (ret = mgr->verifySignature( + ckmPublicKeyAlias, + _tostring(password), + ckmMessage, + ckmSignature, + ckmHashAlgo, + ckmPadding)) != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI int ckmc_get_cert_chain(const ckmc_cert_s *cert, const ckmc_cert_list_s *untrustedcerts, ckmc_cert_list_s **cert_chain_list) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - CKM::CertificateShPtrVector ckmCertChain; + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + CKM::CertificateShPtrVector ckmCertChain; - if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || cert_chain_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || cert_chain_list == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::CertificateShPtr ckmCert = _toCkmCertificate(cert); + CKM::CertificateShPtr ckmCert = _toCkmCertificate(cert); - CKM::CertificateShPtrVector ckmUntrustedCerts = _toCkmCertificateVector(untrustedcerts); + CKM::CertificateShPtrVector ckmUntrustedCerts = _toCkmCertificateVector(untrustedcerts); - ret = mgr->getCertificateChain(ckmCert, ckmUntrustedCerts, EMPTY_CERT_VECTOR, true, ckmCertChain); - if( ret != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + ret = mgr->getCertificateChain(ckmCert, ckmUntrustedCerts, EMPTY_CERT_VECTOR, true, ckmCertChain); + if( ret != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - *cert_chain_list = _toNewCkmCertList(ckmCertChain); + *cert_chain_list = _toNewCkmCertList(ckmCertChain); - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI int ckmc_get_cert_chain_with_alias(const ckmc_cert_s *cert, const ckmc_alias_list_s *untrustedcerts, ckmc_cert_list_s **cert_chain_list) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - CKM::CertificateShPtrVector ckmCertChain; + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + CKM::CertificateShPtrVector ckmCertChain; - if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || cert_chain_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || cert_chain_list == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::CertificateShPtr ckmCert = _toCkmCertificate(cert); - if(ckmCert.get() == NULL) { - return CKMC_ERROR_INVALID_FORMAT; - } + CKM::CertificateShPtr ckmCert = _toCkmCertificate(cert); + if(ckmCert.get() == NULL) { + return CKMC_ERROR_INVALID_FORMAT; + } - CKM::AliasVector ckmUntrustedAliases = _toCkmAliasVector(untrustedcerts); + CKM::AliasVector ckmUntrustedAliases = _toCkmAliasVector(untrustedcerts); - ret = mgr->getCertificateChain(ckmCert, ckmUntrustedAliases, EMPTY_ALIAS_VECTOR, true, ckmCertChain); - if( ret != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + ret = mgr->getCertificateChain(ckmCert, ckmUntrustedAliases, EMPTY_ALIAS_VECTOR, true, ckmCertChain); + if( ret != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - *cert_chain_list = _toNewCkmCertList(ckmCertChain); + *cert_chain_list = _toNewCkmCertList(ckmCertChain); - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI @@ -750,92 +804,104 @@ int ckmc_get_cert_chain_with_trustedcert(const ckmc_cert_s* cert, const bool sys_certs, ckmc_cert_list_s** ppcert_chain_list) { - int ret; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - CKM::CertificateShPtrVector ckm_cert_chain; + return try_catch_enclosure([&]()->int { + int ret; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + CKM::CertificateShPtrVector ckm_cert_chain; - if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || ppcert_chain_list == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + if(cert == NULL || cert->raw_cert == NULL || cert->cert_size <= 0 || ppcert_chain_list == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::CertificateShPtr ckm_cert = _toCkmCertificate(cert); - if(ckm_cert.get() == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + CKM::CertificateShPtr ckm_cert = _toCkmCertificate(cert); + if(ckm_cert.get() == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - CKM::CertificateShPtrVector ckm_untrusted = _toCkmCertificateVector(untrustedcerts); - CKM::CertificateShPtrVector ckm_trusted = _toCkmCertificateVector(trustedcerts); + CKM::CertificateShPtrVector ckm_untrusted = _toCkmCertificateVector(untrustedcerts); + CKM::CertificateShPtrVector ckm_trusted = _toCkmCertificateVector(trustedcerts); - ret = mgr->getCertificateChain(ckm_cert, ckm_untrusted, ckm_trusted, sys_certs, ckm_cert_chain); - if( ret != CKM_API_SUCCESS) { - return to_ckmc_error(ret); - } + ret = mgr->getCertificateChain(ckm_cert, ckm_untrusted, ckm_trusted, sys_certs, ckm_cert_chain); + if( ret != CKM_API_SUCCESS) { + return to_ckmc_error(ret); + } - *ppcert_chain_list = _toNewCkmCertList(ckm_cert_chain); + *ppcert_chain_list = _toNewCkmCertList(ckm_cert_chain); - return CKMC_ERROR_NONE; + return CKMC_ERROR_NONE; + }); } KEY_MANAGER_CAPI int ckmc_ocsp_check(const ckmc_cert_list_s *pcert_chain_list, ckmc_ocsp_status_e *ocsp_status) { - if (pcert_chain_list == NULL - || pcert_chain_list->cert == NULL - || pcert_chain_list->cert->raw_cert == NULL - || pcert_chain_list->cert->cert_size <= 0 - || ocsp_status == NULL) { - return CKMC_ERROR_INVALID_PARAMETER; - } + return try_catch_enclosure([&]()->int { + if (pcert_chain_list == NULL + || pcert_chain_list->cert == NULL + || pcert_chain_list->cert->raw_cert == NULL + || pcert_chain_list->cert->cert_size <= 0 + || ocsp_status == NULL) { + return CKMC_ERROR_INVALID_PARAMETER; + } - int tmpOcspStatus = -1; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - CKM::CertificateShPtrVector ckmCertChain = _toCkmCertificateVector(pcert_chain_list); + int tmpOcspStatus = -1; + CKM::ManagerShPtr mgr = CKM::Manager::create(); + CKM::CertificateShPtrVector ckmCertChain = _toCkmCertificateVector(pcert_chain_list); - int ret = mgr->ocspCheck(ckmCertChain, tmpOcspStatus); - *ocsp_status = to_ckmc_ocsp_status(tmpOcspStatus); - return to_ckmc_error(ret); + int ret = mgr->ocspCheck(ckmCertChain, tmpOcspStatus); + *ocsp_status = to_ckmc_ocsp_status(tmpOcspStatus); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI int ckmc_allow_access(const char *alias, const char *accessor, ckmc_access_right_e granted) { - int ec, permissionMask; - ec = access_to_permission_mask(granted, permissionMask); - if(ec != CKMC_ERROR_NONE) - return ec; + return try_catch_enclosure([&]()->int { + int ec, permissionMask; + ec = access_to_permission_mask(granted, permissionMask); + if(ec != CKMC_ERROR_NONE) + return ec; - return ckmc_set_permission(alias, accessor, permissionMask); + return ckmc_set_permission(alias, accessor, permissionMask); + }); } KEY_MANAGER_CAPI int ckmc_set_permission(const char *alias, const char *accessor, int permissions) { - if (!alias || !accessor) - return CKMC_ERROR_INVALID_PARAMETER; + return try_catch_enclosure([&]()->int { + if (!alias || !accessor) + return CKMC_ERROR_INVALID_PARAMETER; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - return to_ckmc_error(mgr->setPermission(alias, accessor, permissions)); + CKM::ManagerShPtr mgr = CKM::Manager::create(); + return to_ckmc_error(mgr->setPermission(alias, accessor, permissions)); + }); } KEY_MANAGER_CAPI int ckmc_deny_access(const char *alias, const char *accessor) { - if (!alias || !accessor) - return CKMC_ERROR_INVALID_PARAMETER; + return try_catch_enclosure([&]()->int { + if (!alias || !accessor) + return CKMC_ERROR_INVALID_PARAMETER; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - return to_ckmc_error(mgr->setPermission(alias, accessor, CKM::Permission::NONE)); + CKM::ManagerShPtr mgr = CKM::Manager::create(); + return to_ckmc_error(mgr->setPermission(alias, accessor, CKM::Permission::NONE)); + }); } KEY_MANAGER_CAPI int ckmc_remove_alias(const char *alias) { - if(!alias) - return CKMC_ERROR_INVALID_PARAMETER; + return try_catch_enclosure([&]()->int { + if(!alias) + return CKMC_ERROR_INVALID_PARAMETER; - CKM::ManagerShPtr mgr = CKM::Manager::create(); - int ret = mgr->removeAlias(alias); - return to_ckmc_error(ret); + CKM::ManagerShPtr mgr = CKM::Manager::create(); + int ret = mgr->removeAlias(alias); + return to_ckmc_error(ret); + }); } KEY_MANAGER_CAPI @@ -845,12 +911,14 @@ int ckmc_encrypt_data(ckmc_param_list_h params, const ckmc_raw_buffer_s decrypted, ckmc_raw_buffer_s **ppencrypted) { - return _cryptoOperation(&CKM::Manager::encrypt, - params, - key_alias, - password, - decrypted, - ppencrypted); + return try_catch_enclosure([&]()->int { + return _cryptoOperation(&CKM::Manager::encrypt, + params, + key_alias, + password, + decrypted, + ppencrypted); + }); } KEY_MANAGER_CAPI @@ -860,10 +928,12 @@ int ckmc_decrypt_data(ckmc_param_list_h params, const ckmc_raw_buffer_s encrypted, ckmc_raw_buffer_s **ppdecrypted) { - return _cryptoOperation(&CKM::Manager::decrypt, - params, - key_alias, - password, - encrypted, - ppdecrypted); + return try_catch_enclosure([&]()->int { + return _cryptoOperation(&CKM::Manager::decrypt, + params, + key_alias, + password, + encrypted, + ppdecrypted); + }); } diff --git a/src/manager/client/client-manager.cpp b/src/manager/client/client-manager.cpp index 6d8ed4b..275b545 100644 --- a/src/manager/client/client-manager.cpp +++ b/src/manager/client/client-manager.cpp @@ -239,12 +239,10 @@ int Manager::decrypt( ManagerShPtr Manager::create() { try { return std::make_shared(); - } catch (const std::bad_alloc &) { - LogDebug("Bad alloc was caught during Manager::Impl creation."); } catch (...) { - LogError("Critical error: Unknown exception was caught during Manager::Impl creation!"); + LogError("Exception occured in Manager::create"); + throw; } - return ManagerShPtr(); } } // namespace CKM -- 2.7.4 From 84ffd8d75c83103bf2b7c9809297b111445f7a5d Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Thu, 19 Nov 2015 11:43:20 +0100 Subject: [PATCH 16/16] Fix bug found by Klocwork [Problem] The command received from encryption service is deserialized into a variable hiding function argument of the same name. Also the received command was ignored. [Solution] Check if received command is equal to requested one. [Verification] Run ckm-tests --group=CKM_ENCRYPTION_DECRYPTION Change-Id: I16e14dbc8497a9b6ea11d93c8c0a48071562d684 --- src/manager/client/client-manager-impl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/manager/client/client-manager-impl.cpp b/src/manager/client/client-manager-impl.cpp index 790e541..d6a464e 100644 --- a/src/manager/client/client-manager-impl.cpp +++ b/src/manager/client/client-manager-impl.cpp @@ -794,11 +794,11 @@ int Manager::Impl::crypt(EncryptionCommand command, if (CKM_API_SUCCESS != retCode) return retCode; - int command; + int retCommand; int counter; - recv.Deserialize(command, counter, retCode, output); + recv.Deserialize(retCommand, counter, retCode, output); - if (my_counter != counter) { + if (my_counter != counter || retCommand != static_cast(command)) { return CKM_API_ERROR_UNKNOWN; } -- 2.7.4