2 * Copyright (c) 2014-2021 Samsung Electronics Co., Ltd. All rights reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
20 * @brief Sample service implementation.
22 #include <dpl/serialization.h>
23 #include <dpl/log/log.h>
24 #include <ckm/ckm-error.h>
25 #include <ckm/ckm-type.h>
26 #include <key-provider.h>
27 #include <file-system.h>
28 #include <ckm-logic.h>
30 #include <key-aes-impl.h>
31 #include <certificate-config.h>
32 #include <certificate-store.h>
34 #include <sw-backend/store.h>
35 #include <generic-backend/exception.h>
36 #include <ss-migrate.h>
39 const char *const CERT_SYSTEM_DIR = CA_CERTS_DIR;
40 const char *const DEFAULT_UNLOCK_STRING = "cAtRugU7";
42 bool isClientValid(const CKM::ClientId &client)
44 if (client.find(CKM::ALIAS_SEPARATOR) != CKM::ClientId::npos)
50 bool isNameValid(const CKM::Name &name)
52 if (name.find(CKM::ALIAS_SEPARATOR) != CKM::Name::npos)
58 } // anonymous namespace
64 template <int ERROR_ON_CKM_EXCEPTION = CKM_API_ERROR_SERVER_ERROR, class F>
68 static_assert(std::is_same_v<decltype(std::forward<F>(f)()), int>);
69 return std::forward<F>(f)();
70 } catch (const Exc::Exception &e) {
72 } catch (const CKM::Exception &e) {
73 LogError("CKM::Exception: " << e.GetMessage());
74 return ERROR_ON_CKM_EXCEPTION;
78 int toBinaryData(const Crypto::Data &input, Crypto::Data &output)
80 // verify the data integrity
81 if (input.type.isKey()) {
84 if (input.type.isSymmetricKey())
85 output_key = CKM::Key::createAES(input.data);
87 output_key = CKM::Key::create(input.data);
89 if (output_key.get() == NULL) {
90 LogDebug("provided binary data is not valid key data");
91 return CKM_API_ERROR_INPUT_PARAM;
94 output = Crypto::Data(input.type, output_key->getDER());
95 } else if (input.type.isCertificate() || input.type.isChainCert()) {
96 CertificateShPtr cert = CKM::Certificate::create(input.data, DataFormat::FORM_DER);
98 if (cert.get() == NULL) {
99 LogDebug("provided binary data is not valid certificate data");
100 return CKM_API_ERROR_INPUT_PARAM;
103 output = Crypto::Data(input.type, cert->getDER());
108 // TODO: add here BINARY_DATA verification, i.e: max size etc.
109 return CKM_API_SUCCESS;
112 int verifyBinaryData(const Crypto::Data &input)
115 return toBinaryData(input, dummy);
118 int readSingleRow(const Name &name,
119 const ClientId &owner,
121 DB::Crypto &database,
124 DB::Crypto::RowOptional row_optional;
126 if (dataType.isKey()) {
127 // read all key types
128 row_optional = database.getRow(name,
130 DataType::DB_KEY_FIRST,
131 DataType::DB_KEY_LAST);
133 // read anything else
134 row_optional = database.getRow(name,
140 LogDebug("No row for given name, owner and type");
141 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
146 return CKM_API_SUCCESS;
149 int readMultiRow(const Name &name,
150 const ClientId &owner,
152 DB::Crypto &database,
153 DB::RowVector &output)
155 if (dataType.isKey())
156 // read all key types
157 database.getRows(name,
159 DataType::DB_KEY_FIRST,
160 DataType::DB_KEY_LAST,
162 else if (dataType.isChainCert())
163 // read all key types
164 database.getRows(name,
166 DataType::DB_CHAIN_FIRST,
167 DataType::DB_CHAIN_LAST,
170 // read anything else
171 database.getRows(name,
176 if (!output.size()) {
177 LogDebug("No row for given name, owner and type");
178 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
181 return CKM_API_SUCCESS;
186 const uid_t CKMLogic::SYSTEM_DB_UID = 0;
187 const uid_t CKMLogic::ADMIN_USER_DB_UID = 5001;
191 CertificateConfig::addSystemCertificateDir(CERT_SYSTEM_DIR);
193 m_accessControl.updateCCMode();
196 CKMLogic::~CKMLogic() {}
198 void CKMLogic::loadDKEKFile(uid_t user, const Password &password)
200 auto &handle = m_userDataMap[user];
204 auto wrappedDKEK = fs.getDKEK();
206 if (wrappedDKEK.empty()) {
207 wrappedDKEK = KeyProvider::generateDomainKEK(std::to_string(user), password);
208 fs.saveDKEK(wrappedDKEK);
211 handle.keyProvider = KeyProvider(wrappedDKEK, password);
212 if (!handle.keyProvider.isInitialized()) {
213 handle.keyProvider.migrateDomainKEK(wrappedDKEK, password);
214 fs.saveDKEK(handle.keyProvider.getWrappedDomainKEK(password));
215 LogInfo("DKEK migrated");
219 void CKMLogic::saveDKEKFile(uid_t user, const Password &password)
221 auto &handle = m_userDataMap[user];
224 fs.saveDKEK(handle.keyProvider.getWrappedDomainKEK(password));
227 void CKMLogic::migrateSecureStorageData(bool isAdminUser)
229 SsMigration::migrate(isAdminUser, [this](const std::string &name,
230 const Crypto::Data &data,
231 bool adminUserFlag) {
232 LogInfo("Migrate data called with name: " << name);
233 auto ownerId = adminUserFlag ? CLIENT_ID_ADMIN_USER : CLIENT_ID_SYSTEM;
234 auto uid = adminUserFlag ? ADMIN_USER_DB_UID : SYSTEM_DB_UID;
236 int ret = verifyAndSaveDataHelper(Credentials(uid, ownerId), name, ownerId, data,
237 PolicySerializable());
239 if (ret == CKM_API_ERROR_DB_ALIAS_EXISTS)
240 LogWarning("Alias already exist for migrated name: " << name);
241 else if (ret != CKM_API_SUCCESS)
242 LogError("Failed to migrate secure-storage data. name: " << name <<
247 int CKMLogic::unlockDatabase(uid_t user, const Password &password)
249 if (0 < m_userDataMap.count(user) &&
250 m_userDataMap[user].keyProvider.isInitialized())
251 return CKM_API_SUCCESS;
253 int retCode = tryRet([&] {
254 auto &handle = m_userDataMap[user];
257 loadDKEKFile(user, password);
259 auto wrappedDatabaseDEK = fs.getDBDEK();
261 if (wrappedDatabaseDEK.empty()) {
262 wrappedDatabaseDEK = handle.keyProvider.generateDEK(std::to_string(user));
263 fs.saveDBDEK(wrappedDatabaseDEK);
266 RawBuffer key = handle.keyProvider.getPureDEK(wrappedDatabaseDEK);
268 handle.database = DB::Crypto(fs.getLegacyDBPath(), fs.getDBPath(), key);
269 handle.crypto = CryptoLogic();
271 if (!m_accessControl.isSystemService(user)) {
272 // remove data of removed apps during locked state
273 ClientIdVector removedApps = fs.clearRemovedsApps();
275 for (auto &app : removedApps) {
276 handle.crypto.removeKey(app);
277 handle.database.deleteKey(app);
281 if (user == SYSTEM_DB_UID && SsMigration::hasData())
282 migrateSecureStorageData(false);
283 else if (user == ADMIN_USER_DB_UID && SsMigration::hasData())
284 migrateSecureStorageData(true);
286 return CKM_API_SUCCESS;
289 if (CKM_API_SUCCESS != retCode)
290 m_userDataMap.erase(user);
295 int CKMLogic::unlockSystemDB()
297 return unlockDatabase(SYSTEM_DB_UID, DEFAULT_UNLOCK_STRING);
300 UserData &CKMLogic::selectDatabase(const Credentials &cred,
301 const ClientId &owner)
303 // if user trying to access system service - check:
304 // * if user database is unlocked [mandatory]
305 // * if not - proceed with regular user database
306 // * if explicit system database owner given -> switch to system DB
307 if (!m_accessControl.isSystemService(cred)) {
308 if (0 == m_userDataMap.count(cred.clientUid))
309 ThrowErr(Exc::DatabaseLocked, "database with UID: ", cred.clientUid, " locked");
311 if (0 != owner.compare(CLIENT_ID_SYSTEM))
312 return m_userDataMap[cred.clientUid];
315 // system database selected, modify the owner id
316 if (CKM_API_SUCCESS != unlockSystemDB())
317 ThrowErr(Exc::DatabaseLocked, "can not unlock system database");
319 return m_userDataMap[SYSTEM_DB_UID];
322 RawBuffer CKMLogic::unlockUserKey(uid_t user, const Password &password)
324 int retCode = CKM_API_SUCCESS;
326 if (!m_accessControl.isSystemService(user))
327 retCode = unlockDatabase(user, password);
328 else // do not allow lock/unlock operations for system users
329 retCode = CKM_API_ERROR_INPUT_PARAM;
331 return SerializeMessage(retCode);
334 RawBuffer CKMLogic::updateCCMode()
336 m_accessControl.updateCCMode();
337 return SerializeMessage(CKM_API_SUCCESS);
340 RawBuffer CKMLogic::lockUserKey(uid_t user)
342 int retCode = CKM_API_SUCCESS;
344 if (!m_accessControl.isSystemService(user))
345 m_userDataMap.erase(user);
346 else // do not allow lock/unlock operations for system users
347 retCode = CKM_API_ERROR_INPUT_PARAM;
349 return SerializeMessage(retCode);
352 RawBuffer CKMLogic::removeUserData(uid_t user)
354 if (m_accessControl.isSystemService(user))
355 user = SYSTEM_DB_UID;
357 m_userDataMap.erase(user);
359 const int retCode = FileSystem(user).removeUserData()
360 ? CKM_API_ERROR_FILE_SYSTEM
363 return SerializeMessage(retCode);
366 RawBuffer CKMLogic::changeUserPassword(
368 const Password &oldPassword,
369 const Password &newPassword)
371 return SerializeMessage(tryRet([&] {
372 // do not allow to change system database password
373 if (m_accessControl.isSystemService(user))
374 return CKM_API_ERROR_INPUT_PARAM;
376 loadDKEKFile(user, oldPassword);
377 saveDKEKFile(user, newPassword);
379 return CKM_API_SUCCESS;
383 RawBuffer CKMLogic::resetUserPassword(
385 const Password &newPassword)
387 return SerializeMessage(tryRet([&] {
388 // do not allow to reset system database password
389 if (m_accessControl.isSystemService(user))
390 return CKM_API_ERROR_INPUT_PARAM;
392 int retCode = CKM_API_SUCCESS;
394 if (0 == m_userDataMap.count(user)) {
395 // Check if key exists. If exists we must return error
397 auto wrappedDKEKMain = fs.getDKEK();
399 if (!wrappedDKEKMain.empty())
400 retCode = CKM_API_ERROR_BAD_REQUEST;
402 saveDKEKFile(user, newPassword);
409 RawBuffer CKMLogic::removeApplicationData(const ClientId &owner)
411 return SerializeMessage(tryRet([&] {
413 return CKM_API_ERROR_INPUT_PARAM;
415 UidVector uids = FileSystem::getUIDsFromDBFile();
417 for (auto userId : uids) {
418 if (0 == m_userDataMap.count(userId)) {
419 FileSystem fs(userId);
420 fs.addRemovedApp(owner);
422 auto &handle = m_userDataMap[userId];
423 handle.crypto.removeKey(owner);
424 handle.database.deleteKey(owner);
428 return CKM_API_SUCCESS;
432 int CKMLogic::verifyAndSaveDataHelper(
433 const Credentials &cred,
435 const ClientId &owner,
436 const Crypto::Data &data,
437 const PolicySerializable &policy)
440 // check if data is correct
441 Crypto::Data binaryData;
442 int retCode = toBinaryData(data, binaryData);
443 if (retCode != CKM_API_SUCCESS)
446 auto [dbOp, digest, ret] = beginSaveAndGetHash(cred, name, owner);
447 if (ret != CKM_API_SUCCESS)
450 Crypto::GStore &store = m_decider.getStore(binaryData.type, policy);
452 // do not encrypt data with password during cc_mode on
453 Token token = store.import(binaryData,
454 m_accessControl.isCCMode() ? "" : policy.password,
455 Crypto::EncryptionParams(), digest);
456 dbOp.finalize(std::move(token), policy);
457 return CKM_API_SUCCESS;
461 int CKMLogic::getKeyForService(
462 const Credentials &cred,
464 const ClientId &owner,
465 const Password &pass,
466 Crypto::GObjShPtr &key)
469 // Key is for internal service use. It won't be exported to the client
470 Crypto::GObjUPtr obj;
471 int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, owner, pass, obj);
472 if (retCode == CKM_API_SUCCESS)
473 key = std::move(obj);
479 RawBuffer CKMLogic::saveData(
480 const Credentials &cred,
483 const ClientId &owner,
484 const Crypto::Data &data,
485 const PolicySerializable &policy)
487 int retCode = verifyAndSaveDataHelper(cred, name, owner, data, policy);
488 return SerializeMessage(msgId, retCode, data.type);
491 RawBuffer CKMLogic::savePKCS12(
492 const Credentials &cred,
495 const ClientId &owner,
496 const PKCS12Serializable &pkcs,
497 const PolicySerializable &keyPolicy,
498 const PolicySerializable &certPolicy)
500 return SerializeMessage(msgId, tryRet([&] {
501 auto [dbOp, retCode] = beginSave(cred, name, owner);
502 if (retCode != CKM_API_SUCCESS)
505 // extract and encrypt the data
506 DB::RowVector encryptedRows;
508 auto import = [&](const Crypto::Data &data, const Policy& policy){
509 retCode = verifyBinaryData(data);
510 if (retCode != CKM_API_SUCCESS)
513 auto digest = CryptoLogic::makeHash(name, owner, cred.clientUid, data.type);
515 return CKM_API_ERROR_HASH_ERROR;
517 Crypto::GStore &store = m_decider.getStore(data.type, policy);
519 // do not encrypt data with password during cc_mode on
520 Token token = store.import(data,
521 m_accessControl.isCCMode() ? "" : policy.password,
522 Crypto::EncryptionParams(), digest);
524 encryptedRows.push_back(dbOp.encryptOne(std::move(token), policy));
525 return CKM_API_SUCCESS;
528 // private key is mandatory
529 auto key = pkcs.getKey();
531 LogError("Failed to get private key from pkcs");
532 return CKM_API_ERROR_INVALID_FORMAT;
535 Crypto::Data keyData(DataType(key->getType()), key->getDER());
536 retCode = import(keyData, keyPolicy);
537 if (retCode != CKM_API_SUCCESS)
540 // certificate is mandatory
541 auto cert = pkcs.getCertificate();
543 LogError("Failed to get certificate from pkcs");
544 return CKM_API_ERROR_INVALID_FORMAT;
547 Crypto::Data certData(DataType::CERTIFICATE, cert->getDER());
548 retCode = import(certData, certPolicy);
549 if (retCode != CKM_API_SUCCESS)
553 unsigned int cert_index = 0;
554 for (const auto &ca : pkcs.getCaCertificateShPtrVector()) {
555 Crypto::Data caCertData(DataType::getChainDatatype(cert_index ++), ca->getDER());
556 retCode = import(caCertData, certPolicy);
557 if (retCode != CKM_API_SUCCESS)
562 dbOp.database().saveRows(name, owner, encryptedRows);
563 dbOp.transaction().commit();
565 return CKM_API_SUCCESS;
569 RawBuffer CKMLogic::removeData(
570 const Credentials &cred,
573 const ClientId &owner)
575 return SerializeMessage(msgId, tryRet([&] {
576 auto [dbOp, permission, retCode] = beginAndGetPerm(cred, name, owner);
577 if (retCode != CKM_API_SUCCESS)
580 retCode = m_accessControl.canDelete(cred, permission);
581 if (retCode != CKM_API_SUCCESS) {
582 LogWarning("access control check result: " << retCode);
586 // get all matching rows
588 dbOp.database().getRows(name, owner, DataType::DB_FIRST, DataType::DB_LAST, rows);
590 LogDebug("No row for given name and owner");
591 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
594 retCode = dbOp.loadAppKey();
595 if (retCode != CKM_API_SUCCESS)
598 // destroy it in store
599 for (auto &r : rows) {
601 dbOp.handler().crypto.decryptRow(Password(), r);
602 m_decider.getStore(r).destroy(r);
603 } catch (const Exc::AuthenticationFailed &) {
604 LogDebug("Authentication failed when removing data. Ignored.");
609 dbOp.database().deleteRow(name, owner);
610 dbOp.transaction().commit();
612 return CKM_API_SUCCESS;
617 int CKMLogic::checkDataPermissionsHelper(const Credentials &accessorCred,
620 const PermissionMask& permission)
623 return m_accessControl.canExport(accessorCred, row, permission);
625 return m_accessControl.canRead(accessorCred, permission);
628 void CKMLogic::decryptRow(
631 const Password &password,
632 const RawBuffer &hash)
634 if (CryptoLogic::getSchemeVersion(row.encryptionScheme) ==
635 CryptoLogic::ENCRYPTION_V2) {
636 handler.crypto.decryptRow(Password(), row);
638 Crypto::GStore &store = m_decider.getStore(row);
640 Password pass = m_accessControl.isCCMode() ? "" : password;
642 // decrypt entirely with old scheme: b64(pass(appkey(data))) -> data
643 handler.crypto.decryptRow(pass, row);
644 // destroy it in store
647 // import it to store with new scheme: data -> pass(data)
648 Token token = store.import(Crypto::Data(row.dataType, row.data),
650 Crypto::EncryptionParams(),
653 // update row with new token
654 *static_cast<Token *>(&row) = std::move(token);
656 // encrypt it with app key: pass(data) -> b64(appkey(pass(data))
657 auto encryptedRow = handler.crypto.encryptRow(row);
660 handler.database.updateRow(encryptedRow);
664 Crypto::GObjUPtr CKMLogic::rowToObject(
667 const Password &password,
668 const RawBuffer &hash)
670 decryptRow(handler, row, password, hash);
672 return decryptedRowToObj(row, password);
675 int CKMLogic::readDataHelper(
677 const Credentials &cred,
680 const ClientId &owner,
681 const Password &password,
682 Crypto::GObjUPtrVector &objs)
684 auto [dbOp, permission, retCode] = beginAndGetPerm(cred, name, owner);
685 if (retCode != CKM_API_SUCCESS)
690 retCode = readMultiRow(name, owner, dataType, dbOp.database(), rows);
691 if (CKM_API_SUCCESS != retCode)
694 // all read rows belong to the same owner
695 DB::Row &firstRow = rows.at(0);
697 // check access rights
698 retCode = checkDataPermissionsHelper(cred, firstRow, exportFlag, permission);
699 if (CKM_API_SUCCESS != retCode)
702 // for multiple objects add type as hash input (see pkcs12)
703 bool multiple = rows.size() > 1;
707 retCode = dbOp.loadAppKey();
708 if (retCode != CKM_API_SUCCESS)
712 for (auto &row : rows) {
714 digest = CryptoLogic::makeHash(name, owner, cred.clientUid, row.dataType);
716 digest = CryptoLogic::makeHash(name, owner, cred.clientUid);
719 return CKM_API_ERROR_HASH_ERROR;
721 objs.push_back(rowToObject(dbOp.handler(), std::move(row), password, digest));
724 // rowToObject may modify db
725 dbOp.transaction().commit();
727 return CKM_API_SUCCESS;
730 int CKMLogic::readDataHelper(
732 const Credentials &cred,
735 const ClientId &owner,
736 const Password &password,
737 Crypto::GObjUPtr &obj)
739 DataType objDataType;
740 return readDataHelper(exportFlag, cred, dataType, name, owner,
741 password, obj, objDataType);
744 int CKMLogic::readRowHelper(
746 const Credentials &cred,
749 const ClientId &owner,
750 const Password &password,
752 DataType &objDataType)
754 auto [dbOp, permission, retCode] = beginAndGetPerm(cred, name, owner);
755 if (retCode != CKM_API_SUCCESS)
758 retCode = readSingleRow(name, owner, dataType, dbOp.database(), row);
759 if (CKM_API_SUCCESS != retCode)
762 retCode = dbOp.loadAppKey();
763 if (retCode != CKM_API_SUCCESS)
766 objDataType = row.dataType;
768 // check access rights
769 retCode = checkDataPermissionsHelper(cred, row, exportFlag, permission);
770 if (retCode != CKM_API_SUCCESS)
773 auto digest = CryptoLogic::makeHash(name, owner, cred.clientUid);
775 return CKM_API_ERROR_HASH_ERROR;
777 decryptRow(dbOp.handler(), row, password, digest);
779 // decryptRow may modify db
780 dbOp.transaction().commit();
782 return CKM_API_SUCCESS;
785 Crypto::GObjUPtr CKMLogic::decryptedRowToObj(const DB::Row& row, const Password &password)
787 Crypto::GStore &store = m_decider.getStore(row);
789 Password pass = m_accessControl.isCCMode() ? "" : password;
790 return store.getObject(row, pass);
793 int CKMLogic::readDataHelper(
795 const Credentials &cred,
798 const ClientId &owner,
799 const Password &password,
800 Crypto::GObjUPtr &obj,
801 DataType &objDataType)
804 int retCode = readRowHelper(exportFlag, cred, dataType, name, owner, password, row, objDataType);
805 if (retCode != CKM_API_SUCCESS)
808 obj = decryptedRowToObj(row, password);
810 return CKM_API_SUCCESS;
813 RawBuffer CKMLogic::getData(
814 const Credentials &cred,
818 const ClientId &owner,
819 const Password &password)
822 DataType objDataType;
824 int retCode = tryRet([&] {
825 Crypto::GObjUPtr obj;
826 int retCode = readDataHelper(true, cred, dataType, name, owner,
827 password, obj, objDataType);
829 if (retCode == CKM_API_SUCCESS)
830 rowData = obj->getBinary();
835 if (CKM_API_SUCCESS != retCode)
838 return SerializeMessage(msgId, retCode, objDataType, rowData);
841 RawBuffer CKMLogic::getDataProtectionStatus(
842 const Credentials &cred,
846 const ClientId &owner)
849 DataType objDataType;
852 int retCode = tryRet([&] {
853 Crypto::GObjUPtr obj;
854 return readDataHelper(false, cred, dataType, name, owner, password, obj, objDataType);
857 if (retCode == CKM_API_ERROR_AUTHENTICATION_FAILED) {
859 retCode = CKM_API_SUCCESS;
862 return SerializeMessage(msgId, retCode, objDataType, status);
865 RawBuffer CKMLogic::getPKCS12(
866 const Credentials &cred,
869 const ClientId &owner,
870 const Password &keyPassword,
871 const Password &certPassword)
873 PKCS12Serializable output;
875 int retCode = tryRet([&] {
877 CertificateShPtr cert;
878 CertificateShPtrVector caChain;
880 // read private key (mandatory)
881 Crypto::GObjUPtr keyObj;
882 int retCode = readDataHelper(true, cred, DataType::DB_KEY_FIRST, name, owner,
883 keyPassword, keyObj);
885 if (retCode != CKM_API_SUCCESS) {
886 if (retCode != CKM_API_ERROR_NOT_EXPORTABLE)
889 privKey = CKM::Key::create(keyObj->getBinary());
892 // read certificate (mandatory)
893 Crypto::GObjUPtr certObj;
894 retCode = readDataHelper(true, cred, DataType::CERTIFICATE, name, owner,
895 certPassword, certObj);
897 if (retCode != CKM_API_SUCCESS) {
898 if (retCode != CKM_API_ERROR_NOT_EXPORTABLE)
901 cert = CKM::Certificate::create(certObj->getBinary(), DataFormat::FORM_DER);
904 // read CA cert chain (optional)
905 Crypto::GObjUPtrVector caChainObjs;
906 retCode = readDataHelper(true, cred, DataType::DB_CHAIN_FIRST, name, owner,
907 certPassword, caChainObjs);
909 if (retCode != CKM_API_SUCCESS && retCode != CKM_API_ERROR_DB_ALIAS_UNKNOWN) {
910 if (retCode != CKM_API_ERROR_NOT_EXPORTABLE)
913 for (auto &caCertObj : caChainObjs)
914 caChain.push_back(CKM::Certificate::create(caCertObj->getBinary(),
915 DataFormat::FORM_DER));
918 // if anything found, return it
919 if (privKey || cert || caChain.size() > 0)
920 retCode = CKM_API_SUCCESS;
923 if (retCode != CKM_API_SUCCESS)
926 output = PKCS12Serializable(std::move(privKey), std::move(cert), std::move(caChain));
927 return CKM_API_SUCCESS;
930 return SerializeMessage(msgId, retCode, output);
933 int CKMLogic::getAliasInfoListHelper(const Credentials &cred,
934 const DataType dataType,
935 AliasInfoVector &aliasInfoVector)
937 int retCode = CKM_API_ERROR_DB_LOCKED;
939 if (0 < m_userDataMap.count(cred.clientUid)) {
940 auto &database = m_userDataMap[cred.clientUid].database;
942 retCode = tryRet<CKM_API_ERROR_DB_ERROR>([&] {
943 AliasInfoVector tmpVector;
945 if (dataType.isKey()) {
946 // list all key types
947 database.listInfos(cred.client,
949 DataType::DB_KEY_FIRST,
950 DataType::DB_KEY_LAST);
952 // list anything else
953 database.listInfos(cred.client, tmpVector, dataType);
956 aliasInfoVector.insert(aliasInfoVector.end(), tmpVector.begin(), tmpVector.end());
957 return CKM_API_SUCCESS;
964 RawBuffer CKMLogic::getDataList(
965 const Credentials &cred,
969 AliasInfoVector systemVector;
970 AliasInfoVector userVector;
971 AliasInfoVector aliasInfoVector;
973 int retCode = unlockSystemDB();
975 if (CKM_API_SUCCESS == retCode) {
977 if (m_accessControl.isSystemService(cred)) {
979 retCode = getAliasInfoListHelper(Credentials(SYSTEM_DB_UID, CLIENT_ID_SYSTEM),
983 // user - lookup system, then client DB
984 retCode = getAliasInfoListHelper(Credentials(SYSTEM_DB_UID, cred.client),
989 if (retCode == CKM_API_SUCCESS) {
990 retCode = getAliasInfoListHelper(cred,
997 if (retCode == CKM_API_SUCCESS) {
998 aliasInfoVector.insert(aliasInfoVector.end(), systemVector.begin(),
1000 aliasInfoVector.insert(aliasInfoVector.end(), userVector.begin(),
1004 return SerializeMessage(msgId, retCode, dataType, AliasInfoSerializableVector(aliasInfoVector));
1007 int CKMLogic::importInitialData(
1009 const Crypto::Data &data,
1010 const Crypto::EncryptionParams &encParams,
1011 const Policy &policy)
1015 if (encParams.iv.empty() != encParams.tag.empty()) {
1016 LogError("Both iv and tag must be empty or set");
1017 return CKM_API_ERROR_INPUT_PARAM;
1020 // Inital values are always imported with root credentials. Client id is not important.
1021 Credentials rootCred(0, "whatever");
1022 ClientId owner(CLIENT_ID_SYSTEM);
1024 auto [dbOp, digest, retCode] = beginSaveAndGetHash(rootCred, name, owner);
1025 if (retCode != CKM_API_SUCCESS)
1028 Crypto::GStore &store = m_decider.getStore(data.type, policy, true, !encParams.iv.empty());
1031 if (encParams.iv.empty()) {
1032 // Data are not encrypted, let's try to verify them
1033 Crypto::Data binaryData;
1035 if (CKM_API_SUCCESS != (retCode = toBinaryData(data, binaryData)))
1038 token = store.import(binaryData,
1039 m_accessControl.isCCMode() ? "" : policy.password,
1042 token = store.import(data,
1043 m_accessControl.isCCMode() ? "" : policy.password,
1047 dbOp.finalize(std::move(token), policy);
1049 return CKM_API_SUCCESS;
1051 } catch (const std::exception &e) {
1052 LogError("Std::exception: " << e.what());
1053 return CKM_API_ERROR_SERVER_ERROR;
1057 int CKMLogic::DBOperation::loadAppKey(bool keyRequired)
1059 if (!m_handler.crypto.haveKey(m_owner)) {
1060 RawBuffer wrappedDEK;
1061 auto wrappedDEKOptional = m_handler.database.getKey(m_owner);
1063 if (!wrappedDEKOptional) {
1065 LogError("No key for given owner in database");
1066 return CKM_API_ERROR_DB_ERROR;
1068 LogDebug("No Key in database found. Generating new one for client: " << m_owner);
1069 wrappedDEK = m_handler.keyProvider.generateDEK(m_owner);
1070 m_handler.database.saveKey(m_owner, wrappedDEK);
1072 wrappedDEK = *wrappedDEKOptional;
1075 m_handler.crypto.pushKey(m_owner, m_handler.keyProvider.getPureDEK(wrappedDEK));
1078 return CKM_API_SUCCESS;
1081 std::tuple<CKMLogic::DBOperation, int> CKMLogic::begin(
1082 const Credentials &cred,
1084 const ClientId &owner)
1086 auto &handler = selectDatabase(cred, owner);
1087 DBOperation op(handler, name, owner);
1089 if (cred.client.empty() || !isClientValid(cred.client) ||
1090 !isNameValid(name) || !isClientValid(owner))
1091 return std::make_tuple(std::move(op), CKM_API_ERROR_INPUT_PARAM);
1093 return std::make_tuple(std::move(op), CKM_API_SUCCESS);
1096 std::tuple<CKMLogic::DBOperation, PermissionMask, int> CKMLogic::beginAndGetPerm(
1097 const Credentials &cred,
1099 const ClientId &owner)
1101 PermissionMask permission = Permission::NONE;
1102 auto [dbOp, retCode] = begin(cred, name, owner);
1103 if (retCode == CKM_API_SUCCESS)
1104 permission = toPermissionMask(dbOp.database().getPermissionRow(name, owner, cred.client));
1106 return std::make_tuple(std::move(dbOp), permission, retCode);
1109 std::tuple<CKMLogic::DBOperation, int> CKMLogic::beginSave(
1110 const Credentials &cred,
1112 const ClientId &owner)
1114 auto [dbOp, retCode] = begin(cred, name, owner);
1115 if (retCode != CKM_API_SUCCESS)
1116 return std::make_tuple(std::move(dbOp), retCode);
1118 retCode = dbOp.loadAppKey(false);
1119 if (retCode != CKM_API_SUCCESS)
1120 return std::make_tuple(std::move(dbOp), retCode);
1122 // check if accessor is allowed to save owner's items
1123 retCode = m_accessControl.canSave(cred, owner);
1124 if (retCode != CKM_API_SUCCESS) {
1125 LogDebug("accessor " << cred.client << " can not save rows owned by " << owner);
1126 return std::make_tuple(std::move(dbOp), retCode);
1129 if (dbOp.database().isNameOwnerPresent(name, owner))
1130 retCode = CKM_API_ERROR_DB_ALIAS_EXISTS;
1132 return std::make_tuple(std::move(dbOp), retCode);
1135 std::tuple<CKMLogic::DBOperation, RawBuffer, int> CKMLogic::beginSaveAndGetHash(
1136 const Credentials &cred,
1138 const ClientId &owner)
1141 auto [dbOp, retCode] = beginSave(cred, name, owner);
1142 if (retCode == CKM_API_SUCCESS) {
1143 digest = CryptoLogic::makeHash(name, owner, cred.clientUid);
1145 retCode = CKM_API_ERROR_HASH_ERROR;
1148 return std::make_tuple(std::move(dbOp), std::move(digest), retCode);
1151 RawBuffer CKMLogic::createKeyPair(
1152 const Credentials &cred,
1154 const CryptoAlgorithmSerializable &keyGenParams,
1155 const Name &namePrv,
1156 const ClientId &ownerPrv,
1157 const Name &namePub,
1158 const ClientId &ownerPub,
1159 const PolicySerializable &policyPrv,
1160 const PolicySerializable &policyPub)
1162 return SerializeMessage(msgId, tryRet([&] {
1163 auto [dbOpPrv, digestPrv, retCodePrv] = beginSaveAndGetHash(cred, namePrv, ownerPrv);
1164 if (retCodePrv != CKM_API_SUCCESS)
1167 auto [dbOpPub, digestPub, retCodePub] = beginSaveAndGetHash(cred, namePub, ownerPub);
1168 if (retCodePub != CKM_API_SUCCESS)
1171 if (policyPrv.backend != policyPub.backend)
1172 ThrowErr(Exc::InputParam, "Error, key pair must be supported with the same backend.");
1174 bool exportable = policyPrv.extractable || policyPub.extractable;
1175 Policy lessRestricted(Password(), exportable, policyPrv.backend);
1177 // For now any asymmetric key will do. If necessary we can extract it from keyGenParams.
1178 TokenPair keys = m_decider.getStore(DataType::DB_KEY_FIRST, policyPrv, false).generateAKey(
1182 digestPrv, digestPub);
1184 dbOpPrv.finalize(std::move(keys.first), policyPrv);
1185 dbOpPub.finalize(std::move(keys.second), policyPub);
1187 return CKM_API_SUCCESS;
1191 RawBuffer CKMLogic::createKeyAES(
1192 const Credentials &cred,
1196 const ClientId &owner,
1197 const PolicySerializable &policy)
1199 int retCode = CKM_API_SUCCESS;
1202 retCode = tryRet([&] {
1203 auto [dbOp, digest, retCode] = beginSaveAndGetHash(cred, name, owner);
1204 if (retCode != CKM_API_SUCCESS)
1207 // create key in store
1208 CryptoAlgorithm keyGenAlgorithm;
1209 keyGenAlgorithm.setParam(ParamName::ALGO_TYPE, AlgoType::AES_GEN);
1210 keyGenAlgorithm.setParam(ParamName::GEN_KEY_LEN, size);
1212 auto& store = m_decider.getStore(DataType::KEY_AES, policy, false);
1213 Token key = store.generateSKey(keyGenAlgorithm, policy.password, digest);
1215 dbOp.finalize(std::move(key), policy);
1216 return CKM_API_SUCCESS;
1218 } catch (std::invalid_argument &e) {
1219 LogDebug("invalid argument error: " << e.what());
1220 retCode = CKM_API_ERROR_INPUT_PARAM;
1223 return SerializeMessage(msgId, retCode);
1226 int CKMLogic::readCertificateHelper(
1227 const Credentials &cred,
1228 const OwnerNameVector &ownerNameVector,
1229 CertificateImplVector &certVector)
1231 for (auto &i : ownerNameVector) {
1232 // certificates can't be protected with custom user password
1233 Crypto::GObjUPtr obj;
1235 ec = readDataHelper(true,
1237 DataType::CERTIFICATE,
1239 cred.effectiveOwner(i.first),
1243 if (ec != CKM_API_SUCCESS)
1246 certVector.emplace_back(obj->getBinary(), DataFormat::FORM_DER);
1248 // try to read chain certificates (if present)
1249 Crypto::GObjUPtrVector caChainObjs;
1250 ec = readDataHelper(true,
1252 DataType::DB_CHAIN_FIRST,
1254 cred.effectiveOwner(i.first),
1258 if (ec != CKM_API_SUCCESS && ec != CKM_API_ERROR_DB_ALIAS_UNKNOWN)
1261 for (auto &caCertObj : caChainObjs)
1262 certVector.emplace_back(caCertObj->getBinary(), DataFormat::FORM_DER);
1265 return CKM_API_SUCCESS;
1268 int CKMLogic::getCertificateChainHelper(
1269 const CertificateImpl &cert,
1270 const RawBufferVector &untrustedCertificates,
1271 const RawBufferVector &trustedCertificates,
1272 bool useTrustedSystemCertificates,
1273 RawBufferVector &chainRawVector)
1275 CertificateImplVector untrustedCertVector;
1276 CertificateImplVector trustedCertVector;
1277 CertificateImplVector chainVector;
1280 return CKM_API_ERROR_INPUT_PARAM;
1282 for (auto &e : untrustedCertificates) {
1283 CertificateImpl c(e, DataFormat::FORM_DER);
1286 return CKM_API_ERROR_INPUT_PARAM;
1288 untrustedCertVector.push_back(std::move(c));
1291 for (auto &e : trustedCertificates) {
1292 CertificateImpl c(e, DataFormat::FORM_DER);
1295 return CKM_API_ERROR_INPUT_PARAM;
1297 trustedCertVector.push_back(std::move(c));
1300 CertificateStore store;
1301 int retCode = store.verifyCertificate(cert,
1302 untrustedCertVector,
1304 useTrustedSystemCertificates,
1305 m_accessControl.isCCMode(),
1308 if (retCode != CKM_API_SUCCESS)
1311 for (auto &e : chainVector)
1312 chainRawVector.push_back(e.getDER());
1314 return CKM_API_SUCCESS;
1317 int CKMLogic::getCertificateChainHelper(
1318 const Credentials &cred,
1319 const CertificateImpl &cert,
1320 const OwnerNameVector &untrusted,
1321 const OwnerNameVector &trusted,
1322 bool useTrustedSystemCertificates,
1323 RawBufferVector &chainRawVector)
1325 CertificateImplVector untrustedCertVector;
1326 CertificateImplVector trustedCertVector;
1327 CertificateImplVector chainVector;
1330 return CKM_API_ERROR_INPUT_PARAM;
1332 int retCode = readCertificateHelper(cred, untrusted, untrustedCertVector);
1334 if (retCode != CKM_API_SUCCESS)
1337 retCode = readCertificateHelper(cred, trusted, trustedCertVector);
1339 if (retCode != CKM_API_SUCCESS)
1342 CertificateStore store;
1343 retCode = store.verifyCertificate(cert,
1344 untrustedCertVector,
1346 useTrustedSystemCertificates,
1347 m_accessControl.isCCMode(),
1350 if (retCode != CKM_API_SUCCESS)
1353 for (auto &i : chainVector)
1354 chainRawVector.push_back(i.getDER());
1356 return CKM_API_SUCCESS;
1359 RawBuffer CKMLogic::getCertificateChain(
1360 const Credentials & /*cred*/,
1362 const RawBuffer &certificate,
1363 const RawBufferVector &untrustedCertificates,
1364 const RawBufferVector &trustedCertificates,
1365 bool useTrustedSystemCertificates)
1367 CertificateImpl cert(certificate, DataFormat::FORM_DER);
1368 RawBufferVector chainRawVector;
1369 int retCode = CKM_API_ERROR_UNKNOWN;
1372 retCode = getCertificateChainHelper(cert,
1373 untrustedCertificates,
1374 trustedCertificates,
1375 useTrustedSystemCertificates,
1377 } catch (const Exc::Exception &e) {
1378 retCode = e.error();
1379 } catch (const std::exception &e) {
1380 LogError("STD exception " << e.what());
1381 retCode = CKM_API_ERROR_SERVER_ERROR;
1383 LogError("Unknown error.");
1386 return SerializeMessage(msgId, retCode, chainRawVector);
1389 RawBuffer CKMLogic::getCertificateChain(
1390 const Credentials &cred,
1392 const RawBuffer &certificate,
1393 const OwnerNameVector &untrustedCertificates,
1394 const OwnerNameVector &trustedCertificates,
1395 bool useTrustedSystemCertificates)
1397 int retCode = CKM_API_ERROR_UNKNOWN;
1398 CertificateImpl cert(certificate, DataFormat::FORM_DER);
1399 RawBufferVector chainRawVector;
1402 retCode = getCertificateChainHelper(cred,
1404 untrustedCertificates,
1405 trustedCertificates,
1406 useTrustedSystemCertificates,
1408 } catch (const Exc::Exception &e) {
1409 retCode = e.error();
1410 } catch (const std::exception &e) {
1411 LogError("STD exception " << e.what());
1412 retCode = CKM_API_ERROR_SERVER_ERROR;
1414 LogError("Unknown error.");
1417 return SerializeMessage(msgId, retCode, chainRawVector);
1420 RawBuffer CKMLogic::createSignature(
1421 const Credentials &cred,
1423 const Name &privateKeyName,
1424 const ClientId &owner,
1425 const Password &password, // password for private_key
1426 const RawBuffer &message,
1427 const CryptoAlgorithm &cryptoAlg)
1429 RawBuffer signature;
1431 int retCode = CKM_API_SUCCESS;
1434 retCode = tryRet([&] {
1435 Crypto::GObjUPtr obj;
1436 int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, privateKeyName,
1437 owner, password, obj);
1439 if (retCode == CKM_API_SUCCESS)
1440 signature = obj->sign(cryptoAlg, message);
1444 } catch (const std::exception &e) {
1445 LogError("STD exception " << e.what());
1446 retCode = CKM_API_ERROR_SERVER_ERROR;
1449 return SerializeMessage(msgId, retCode, signature);
1452 RawBuffer CKMLogic::verifySignature(
1453 const Credentials &cred,
1455 const Name &publicKeyOrCertName,
1456 const ClientId &owner,
1457 const Password &password, // password for public_key (optional)
1458 const RawBuffer &message,
1459 const RawBuffer &signature,
1460 const CryptoAlgorithm ¶ms)
1462 return SerializeMessage(msgId, tryRet([&] {
1463 // try certificate first - looking for a public key.
1464 // in case of PKCS, pub key from certificate will be found first
1465 // rather than private key from the same PKCS.
1466 Crypto::GObjUPtr obj;
1467 int retCode = readDataHelper(false, cred, DataType::CERTIFICATE,
1468 publicKeyOrCertName, owner, password, obj);
1470 if (retCode == CKM_API_ERROR_DB_ALIAS_UNKNOWN)
1471 retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST,
1472 publicKeyOrCertName, owner, password, obj);
1474 if (retCode == CKM_API_SUCCESS)
1475 retCode = obj->verify(params, message, signature);
1481 int CKMLogic::setPermissionHelper(
1482 const Credentials &cred, // who's the client
1484 const ClientId &owner, // who's the owner
1485 const ClientId &accessor, // who will get the access
1486 const PermissionMask permissionMask)
1488 auto [dbOp, retCode] = beginSave(cred, name, owner);
1489 // Normally, saving requires alias to be unoccupied. When changing permissions it's the opposite
1490 if (retCode != CKM_API_ERROR_DB_ALIAS_EXISTS) {
1491 if (retCode == CKM_API_SUCCESS)
1492 retCode = CKM_API_ERROR_DB_ALIAS_UNKNOWN;
1496 // currently we don't support modification of owner's permissions to his own rows
1497 if (owner == accessor)
1498 return CKM_API_ERROR_INPUT_PARAM;
1500 // system database does not support write/remove permissions
1501 if ((0 == owner.compare(CLIENT_ID_SYSTEM)) && (permissionMask & Permission::REMOVE))
1502 return CKM_API_ERROR_INPUT_PARAM;
1504 // set permissions to the row owned by owner for accessor
1505 dbOp.database().setPermission(name, owner, accessor, permissionMask);
1506 dbOp.transaction().commit();
1508 return CKM_API_SUCCESS;
1511 RawBuffer CKMLogic::setPermission(
1512 const Credentials &cred,
1515 const ClientId &owner,
1516 const ClientId &accessor,
1517 const PermissionMask permissionMask)
1519 return SerializeMessage(msgID, tryRet([&] {
1520 return setPermissionHelper(cred, name, owner, accessor, permissionMask);
1524 RawBuffer CKMLogic::deriveKey(
1525 const Credentials &cred,
1527 const CryptoAlgorithm ¶ms,
1528 const Name &secretName,
1529 const ClientId &secretOwner,
1530 const Password &secretPassword,
1531 const Name &newKeyName,
1532 const ClientId &newKeyOwner,
1533 const Policy &newKeyPolicy)
1535 return SerializeMessage(msgID, tryRet([&] {
1536 // Get key/secret for internal service use. It won't be exported to the client
1537 Crypto::GObjUPtr obj;
1539 int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST,
1540 secretName, secretOwner, secretPassword, obj, objType);
1541 if (retCode != CKM_API_SUCCESS) {
1542 if (retCode != CKM_API_ERROR_DB_ALIAS_UNKNOWN)
1545 retCode = readDataHelper(false, cred, DataType::BINARY_DATA,
1546 secretName, secretOwner, secretPassword, obj, objType);
1547 if (retCode != CKM_API_SUCCESS)
1551 auto [dbOp, digest, ret] = beginSaveAndGetHash(cred, newKeyName, newKeyOwner);
1552 if (ret != CKM_API_SUCCESS)
1555 // ECDH (private key) -> binary secret, KBKDF -> symmetric key
1556 DataType newKeyType = objType.isKeyPrivate() ? DataType::BINARY_DATA : DataType::KEY_AES;
1557 if (!m_decider.checkStore(obj->backendId(), newKeyType, newKeyPolicy, false)) {
1558 LogDebug("Can't import the derived key to backend " <<
1559 static_cast<int>(obj->backendId()) << " with given policy");
1560 return CKM_API_ERROR_INPUT_PARAM;
1564 Token derived = obj->derive(params, newKeyPolicy.password, digest);
1566 dbOp.finalize(std::move(derived), newKeyPolicy);
1568 return CKM_API_SUCCESS;
1572 RawBuffer CKMLogic::importWrappedKey(
1573 const Credentials &cred,
1575 const CryptoAlgorithm ¶ms,
1576 const Name &wrappingKeyName,
1577 const ClientId &wrappingKeyOwner,
1578 const Password &wrappingKeyPassword,
1579 const Name &keyName,
1580 const ClientId &keyOwner,
1581 const RawBuffer &wrappedKey,
1582 const CKM::DataType keyType,
1583 const PolicySerializable &policy)
1585 return SerializeMessage(msgId, tryRet([&] {
1586 Crypto::GObjUPtr wrappingKey;
1588 auto [dbOp, digest, retCode] = beginSaveAndGetHash(cred, keyName, keyOwner);
1589 if (retCode != CKM_API_SUCCESS)
1592 retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, wrappingKeyName,
1593 wrappingKeyOwner, wrappingKeyPassword, wrappingKey);
1594 if (retCode != CKM_API_SUCCESS)
1597 if (!m_decider.checkStore(wrappingKey->backendId(), keyType, policy, true)) {
1598 LogDebug("Can't import the wrapped key to backend " <<
1599 static_cast<int>(wrappingKey->backendId()) << " with given policy");
1600 return CKM_API_ERROR_INPUT_PARAM;
1603 Token token = wrappingKey->unwrap(params,
1604 Crypto::Data(keyType, std::move(wrappedKey)),
1608 dbOp.finalize(std::move(token), policy);
1614 RawBuffer CKMLogic::exportWrappedKey(
1615 const Credentials &cred,
1617 const CryptoAlgorithm ¶ms,
1618 const Name &wrappingKeyName,
1619 const ClientId &wrappingKeyOwner,
1620 const Password &wrappingKeyPassword,
1621 const Name &keyName,
1622 const ClientId &keyOwner,
1623 const Password &keyPassword)
1625 Crypto::GObjUPtr wrappingKey;
1626 DB::Row wrappedKeyRow;
1627 DataType wrappedKeyType;
1628 RawBuffer wrappedKey;
1630 auto retCode = tryRet([&] {
1631 auto retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, wrappingKeyName,
1632 wrappingKeyOwner, wrappingKeyPassword, wrappingKey);
1633 if (retCode != CKM_API_SUCCESS)
1636 retCode = readRowHelper(false, cred, DataType::DB_KEY_FIRST, keyName,
1637 keyOwner, keyPassword, wrappedKeyRow, wrappedKeyType);
1638 if (retCode != CKM_API_SUCCESS)
1641 wrappedKey = wrappingKey->wrap(params, wrappedKeyRow, keyPassword);
1646 return SerializeMessage(msgID, retCode, wrappedKeyType, wrappedKey);