From 64fdd666ffad209bf51b73530b80e6868507e67c Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Mon, 14 Sep 2015 20:33:12 -0500 Subject: [PATCH] Add ability to decrypt multiple data objects in the same session using a cryptographic card Add LUKS key verification routine --- tdecore/tdehw/tdecryptographiccarddevice.cpp | 164 ++++++++++++++---- tdecore/tdehw/tdecryptographiccarddevice.h | 17 +- .../tdecryptographiccarddevice_private.h | 2 + tdecore/tdehw/tdestoragedevice.cpp | 31 ++++ tdecore/tdehw/tdestoragedevice.h | 16 +- 5 files changed, 197 insertions(+), 33 deletions(-) diff --git a/tdecore/tdehw/tdecryptographiccarddevice.cpp b/tdecore/tdehw/tdecryptographiccarddevice.cpp index 99c00b9e5..37aa6cac5 100644 --- a/tdecore/tdehw/tdecryptographiccarddevice.cpp +++ b/tdecore/tdehw/tdecryptographiccarddevice.cpp @@ -40,6 +40,8 @@ // 1 second #define PCSC_POLL_TIMEOUT_S 1000 +#define CARD_MAX_LOGIN_RETRY_COUNT 3 + /* FIXME * This is incomplete */ @@ -63,6 +65,7 @@ CryptoCardDeviceWatcher::CryptoCardDeviceWatcher() { #endif m_cardPINPromptDone = true; m_pinCallbacksEnabled = false; + m_cardReusePIN = false; } CryptoCardDeviceWatcher::~CryptoCardDeviceWatcher() { @@ -196,6 +199,14 @@ void CryptoCardDeviceWatcher::setProvidedPin(TQString pin) { m_cardPINPromptDone = true; } +void CryptoCardDeviceWatcher::retrySamePin(bool enable) { + m_cardReusePIN = enable; + if (!enable) { + m_cardPIN = "SHREDDINGTHEPINISMOSTSECURE"; + m_cardPIN = TQString::null; + } +} + TQString CryptoCardDeviceWatcher::getCardATR(TQString readerName) { #ifdef WITH_PCSC unsigned int i; @@ -247,6 +258,10 @@ TQString CryptoCardDeviceWatcher::doPinRequest(TQString prompt) { return TQString::null; } + if (m_cardReusePIN) { + return m_cardPIN; + } + m_cardPINPromptDone = false; emit(pinRequested(prompt)); while (!m_cardPINPromptDone) { @@ -269,6 +284,7 @@ static void pkcs_log_hook(IN void * const global_data, IN unsigned flags, IN con static PKCS11H_BOOL pkcs_pin_hook(IN void * const global_data, IN void * const user_data, IN const pkcs11h_token_id_t token, IN const unsigned retry, OUT char * const pin, IN const size_t pin_max) { CryptoCardDeviceWatcher* watcher = (CryptoCardDeviceWatcher*)global_data; + TQString providedPin = watcher->doPinRequest(i18n("Please enter the PIN for '%1'").arg(token->display)); if (providedPin.length() > 0) { snprintf(pin, pin_max, "%s", providedPin.ascii()); @@ -307,6 +323,11 @@ int CryptoCardDeviceWatcher::initializePkcs() { } #endif + if ((rv = pkcs11h_setMaxLoginRetries(CARD_MAX_LOGIN_RETRY_COUNT)) != CKR_OK) { + printf("pkcs11h_setMaxLoginRetries failed: %s\n", pkcs11h_getMessage(rv)); + return -1; + } + if ((rv = pkcs11h_setPINPromptHook(pkcs_pin_hook, this)) != CKR_OK) { printf("pkcs11h_setPINPromptHook failed: %s\n", pkcs11h_getMessage(rv)); return -1; @@ -337,7 +358,7 @@ int CryptoCardDeviceWatcher::retrieveCardCertificates(TQString readerName) { return -1; } - rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, &issuers, &certs); + rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_PIN_PROMPT, &issuers, &certs); if ((rv != CKR_OK) || (certs == NULL)) { printf("Cannot enumerate certificates: %s\n", pkcs11h_getMessage(rv)); return -1; @@ -350,7 +371,7 @@ int CryptoCardDeviceWatcher::retrieveCardCertificates(TQString readerName) { printf("Certificate %d name: '%s'\n", i, label.ascii()); pkcs11h_certificate_t certificate; - rv = pkcs11h_certificate_create(certs->certificate_id, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &certificate); + rv = pkcs11h_certificate_create(certs->certificate_id, NULL, PKCS11H_PROMPT_MASK_ALLOW_PIN_PROMPT, PKCS11H_PIN_CACHE_INFINITE, &certificate); if (rv != CKR_OK) { printf("Cannot read certificate: %s\n", pkcs11h_getMessage(rv)); pkcs11h_certificate_freeCertificateId(certs->certificate_id); @@ -542,6 +563,19 @@ void TDECryptographicCardDevice::workerRequestedPin(TQString prompt) { } int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArray &ciphertext, TQByteArray &plaintext, TQString *errstr) { + TQValueList cipherTextList; + TQValueList plainTextList; + TQValueList retCodeList; + + cipherTextList.append(ciphertext); + + this->decryptDataEncryptedWithCertPublicKey(cipherTextList, plainTextList, retCodeList, errstr); + + plaintext = plainTextList[0]; + return retCodeList[0]; +} + +int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQValueList &cipherTextList, TQValueList &plainTextList, TQValueList &retcodes, TQString *errstr) { #if WITH_PKCS int ret = -1; @@ -559,7 +593,7 @@ int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArra return -1; } - rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, &issuers, &certs); + rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_PIN_PROMPT, &issuers, &certs); if ((rv != CKR_OK) || (certs == NULL)) { if (errstr) *errstr = i18n("Cannot enumerate certificates: %1").arg(pkcs11h_getMessage(rv)); return -1; @@ -570,7 +604,7 @@ int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArra TQString label = cert->certificate_id->displayName; pkcs11h_certificate_t certificate; - rv = pkcs11h_certificate_create(certs->certificate_id, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &certificate); + rv = pkcs11h_certificate_create(certs->certificate_id, NULL, PKCS11H_PROMPT_MASK_ALLOW_PIN_PROMPT, PKCS11H_PIN_CACHE_INFINITE, &certificate); if (rv != CKR_OK) { if (errstr) *errstr = i18n("Cannot read certificate: %1").arg(pkcs11h_getMessage(rv)); pkcs11h_certificate_freeCertificateId(certs->certificate_id); @@ -588,17 +622,11 @@ int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArra break; } - if (ciphertext.size() < 16) { - if (errstr) *errstr = i18n("Cannot decrypt: %1").arg(i18n("Ciphertext too small")); - ret = -2; - continue; - } - // Get certificate data X509* x509_local; x509_local = pkcs11h_openssl_session_getX509(openssl_session); if (!x509_local) { - if (errstr) *errstr = i18n("Cannot get X509 object\n"); + if (errstr) *errstr = i18n("Cannot get X509 object"); ret = -1; } @@ -610,34 +638,105 @@ int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArra rsa_pubkey = EVP_PKEY_get1_RSA(x509_pubkey); } - // Try to get RSA parameters - if (rsa_pubkey) { - unsigned int rsa_length = RSA_size(rsa_pubkey); - if (ciphertext.size() > rsa_length) { - if (errstr) *errstr = i18n("Cannot decrypt: %1").arg(i18n("Ciphertext too large")); + // Check PIN + rv = pkcs11h_certificate_ensureKeyAccess(certificate); + if (rv != CKR_OK) { + if (rv == CKR_CANCEL) { + ret = -3; + break; + } + else if ((rv == CKR_PIN_INCORRECT) || (rv == CKR_USER_NOT_LOGGED_IN)) { ret = -2; - continue; + break; + } + else { + ret = -2; + break; } } - size_t size = 0; - // Determine output buffer size - rv = pkcs11h_certificate_decryptAny(certificate, CKM_RSA_PKCS, (unsigned char*)ciphertext.data(), ciphertext.size(), NULL, &size); - if (rv != CKR_OK) { - if (errstr) *errstr = i18n("Cannot determine decrypted message length: %1").arg(pkcs11h_getMessage(rv)); - ret = -2; + // We know the cached PIN is correct; disable any further login prompts + m_watcherObject->retrySamePin(true); + + TQValueList::iterator it; + TQValueList::iterator it2; + TQValueList::iterator it3; + plainTextList.clear(); + retcodes.clear(); + for (it = cipherTextList.begin(); it != cipherTextList.end(); ++it) { + plainTextList.append(TQByteArray()); + retcodes.append(-1); } - else { - // Decrypt data - plaintext.resize(size); - rv = pkcs11h_certificate_decryptAny(certificate, CKM_RSA_PKCS, (unsigned char*)ciphertext.data(), ciphertext.size(), (unsigned char*)plaintext.data(), &size); - if (rv != CKR_OK) { - if (errstr) *errstr = i18n("Cannot decrypt: %1").arg(pkcs11h_getMessage(rv)); + for (it = cipherTextList.begin(), it2 = plainTextList.begin(), it3 = retcodes.begin(); it != cipherTextList.end(); ++it, ++it2, ++it3) { + TQByteArray& ciphertext = *it; + TQByteArray& plaintext = *it2; + int& retcode = *it3; + + // Verify minimum size + if (ciphertext.size() < 16) { + if (errstr) *errstr = i18n("Cannot decrypt: %1").arg(i18n("Ciphertext too small")); ret = -2; + retcode = -2; + continue; + } + + // Try to get RSA parameters and verify maximum size + if (rsa_pubkey) { + unsigned int rsa_length = RSA_size(rsa_pubkey); + if (ciphertext.size() > rsa_length) { + if (errstr) *errstr = i18n("Cannot decrypt: %1").arg(i18n("Ciphertext too large")); + ret = -2; + retcode = -2; + continue; + } + } + + size_t size = 0; + // Determine output buffer size + rv = pkcs11h_certificate_decryptAny(certificate, CKM_RSA_PKCS, (unsigned char*)ciphertext.data(), ciphertext.size(), NULL, &size); + if (rv != CKR_OK) { + if (errstr) *errstr = i18n("Cannot determine decrypted message length: %1 (%2)").arg(pkcs11h_getMessage(rv)).arg(rv); + if (rv == CKR_CANCEL) { + ret = -3; + retcode = -3; + break; + } + else if ((rv == CKR_PIN_INCORRECT) || (rv == CKR_USER_NOT_LOGGED_IN)) { + ret = -2; + retcode = -2; + break; + } + else { + ret = -2; + retcode = -2; + } } else { - if (errstr) *errstr = TQString::null; - ret = 0; + // Decrypt data + plaintext.resize(size); + rv = pkcs11h_certificate_decryptAny(certificate, CKM_RSA_PKCS, (unsigned char*)ciphertext.data(), ciphertext.size(), (unsigned char*)plaintext.data(), &size); + if (rv != CKR_OK) { + if (errstr) *errstr = i18n("Cannot decrypt: %1 (%2)").arg(pkcs11h_getMessage(rv)).arg(rv); + if (rv == CKR_CANCEL) { + ret = -3; + retcode = -3; + break; + } + else if ((rv == CKR_PIN_INCORRECT) || (rv == CKR_USER_NOT_LOGGED_IN)) { + ret = -2; + retcode = -2; + break; + } + else { + ret = -2; + retcode = -2; + } + } + else { + if (errstr) *errstr = TQString::null; + ret = 0; + retcode = 0; + } } } @@ -653,6 +752,9 @@ int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArra } pkcs11h_certificate_freeCertificateIdList(issuers); + // Restore normal login attempt method + m_watcherObject->retrySamePin(false); + return ret; #else return -1; diff --git a/tdecore/tdehw/tdecryptographiccarddevice.h b/tdecore/tdehw/tdecryptographiccarddevice.h index fd5256d23..db0f08289 100644 --- a/tdecore/tdehw/tdecryptographiccarddevice.h +++ b/tdecore/tdehw/tdecryptographiccarddevice.h @@ -110,13 +110,28 @@ class TDECORE_EXPORT TDECryptographicCardDevice : public TDEGenericDevice * decrypt data originally encrypted using a public key from one of the certificates * stored on the card. * This operation takes place on the card, and in most cases will require PIN entry. + * This method decrypts one data object only * @param ciphertext Encrypted data * @param plaintext Decrypted data * @param errstr Pointer to TQString to be loaded with error description on failure - * @return 0 on success, -1 on general failure, -2 on encryption failure + * @return 0 on success, -1 on general failure, -2 on encryption failure, -3 on user cancel */ int decryptDataEncryptedWithCertPublicKey(TQByteArray &ciphertext, TQByteArray &plaintext, TQString *errstr=NULL); + /** + * If monitoring of insert / remove events is enabled, and a card has been inserted, + * decrypt data originally encrypted using a public key from one of the certificates + * stored on the card. + * This operation takes place on the card, and in most cases will require PIN entry. + * This method is used to decrypt multiple data objects in one pass. + * @param cipherTextList Encrypted data object list + * @param plainTextList Decrypted data object list + * @param retcodes Return code for each data object + * @param errstr Pointer to TQString to be loaded with error description on failure + * @return 0 on success, -1 on general failure, -2 on encryption failure, -3 on user cancel + */ + int decryptDataEncryptedWithCertPublicKey(TQValueList &cipherTextList, TQValueList &plainTextList, TQValueList &retcodes, TQString *errstr); + /** * Create a new random key and encrypt with the public key * contained in the given certificate. diff --git a/tdecore/tdehw/tdecryptographiccarddevice_private.h b/tdecore/tdehw/tdecryptographiccarddevice_private.h index 3371098f2..b7d38fe52 100644 --- a/tdecore/tdehw/tdecryptographiccarddevice_private.h +++ b/tdecore/tdehw/tdecryptographiccarddevice_private.h @@ -59,6 +59,7 @@ class CryptoCardDeviceWatcher : public TQObject int initializePkcs(); TQString doPinRequest(TQString prompt); void setProvidedPin(TQString pin); + void retrySamePin(bool enable); void enablePINEntryCallbacks(bool enable); public: @@ -72,6 +73,7 @@ class CryptoCardDeviceWatcher : public TQObject bool m_pinCallbacksEnabled; TQString m_cardPIN; bool m_cardPINPromptDone; + bool m_cardReusePIN; #ifdef WITH_PCSC SCARDCONTEXT m_cardContext; SCARD_READERSTATE *m_readerStates; diff --git a/tdecore/tdehw/tdestoragedevice.cpp b/tdecore/tdehw/tdestoragedevice.cpp index 1b4be9283..ff309f0f5 100644 --- a/tdecore/tdehw/tdestoragedevice.cpp +++ b/tdecore/tdehw/tdestoragedevice.cpp @@ -179,11 +179,16 @@ void TDEStorageDevice::internalInitializeLUKSIfNeeded() { } void TDEStorageDevice::cryptSetOperationsUnlockPassword(TQByteArray password) { +#if defined(WITH_CRYPTSETUP) + crypt_memory_lock(NULL, 1); m_cryptDevicePassword = password; +#endif } void TDEStorageDevice::cryptClearOperationsUnlockPassword() { + m_cryptDevicePassword.fill(0); m_cryptDevicePassword.resize(0); + crypt_memory_lock(NULL, 0); } bool TDEStorageDevice::cryptOperationsUnlockPasswordSet() { @@ -195,6 +200,32 @@ bool TDEStorageDevice::cryptOperationsUnlockPasswordSet() { } } +TDELUKSResult::TDELUKSResult TDEStorageDevice::cryptCheckKey(unsigned int keyslot) { +#if defined(WITH_CRYPTSETUP) + int ret; + + if (m_cryptDevice) { + if (keyslot < m_cryptKeySlotCount) { + ret = crypt_activate_by_passphrase(m_cryptDevice, NULL, keyslot, m_cryptDevicePassword.data(), m_cryptDevicePassword.size(), 0); + if (ret < 0) { + return TDELUKSResult::KeyslotOpFailed; + } + else { + return TDELUKSResult::Success; + } + } + else { + return TDELUKSResult::InvalidKeyslot; + } + } + else { + return TDELUKSResult::LUKSNotFound; + } +#else + return TDELUKSResult::LUKSNotSupported; +#endif +} + TDELUKSResult::TDELUKSResult TDEStorageDevice::cryptAddKey(unsigned int keyslot, TQByteArray password) { #if defined(WITH_CRYPTSETUP) int ret; diff --git a/tdecore/tdehw/tdestoragedevice.h b/tdecore/tdehw/tdestoragedevice.h index d3d797686..b5922eec9 100644 --- a/tdecore/tdehw/tdestoragedevice.h +++ b/tdecore/tdehw/tdestoragedevice.h @@ -368,13 +368,27 @@ class TDECORE_EXPORT TDEStorageDevice : public TDEGenericDevice bool cryptOperationsUnlockPasswordSet(); /** - * Adds a new key to the specific keyslot, overwriting the existing key if present + * Checks the preloaded unlock password against the specified keyslot + * + * @param keyslot Keyslot number + * @return TDELUKSResult::TDELUKSResult containing the status code returned + * from the operation, or TDELUKSResult::LUKSNotSupported if LUKS support unavailable + * @return TDELUKSResult::Success on success + * + * @see cryptSetOperationsUnlockPassword + */ + TDELUKSResult::TDELUKSResult cryptCheckKey(unsigned int keyslot); + + /** + * Adds a new key to the specified keyslot, overwriting the existing key if present * * @param keyslot New keyslot number * @param password New keyslot password * @return TDELUKSResult::TDELUKSResult containing the status code returned * from the operation, or TDELUKSResult::LUKSNotSupported if LUKS support unavailable * @return TDELUKSResult::Success on success + * + * @see cryptSetOperationsUnlockPassword */ TDELUKSResult::TDELUKSResult cryptAddKey(unsigned int keyslot, TQByteArray password);