From 402781f094e9a2450942f9d58215da281ba080c2 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Mon, 14 Sep 2015 15:08:14 -0500 Subject: Add cryptographic card decryption method to tdehwlib --- tdecore/tdehw/tdecryptographiccarddevice.cpp | 227 +++++++++++++++++++-- tdecore/tdehw/tdecryptographiccarddevice.h | 37 ++++ tdecore/tdehw/tdecryptographiccarddevice_private.h | 10 + 3 files changed, 259 insertions(+), 15 deletions(-) diff --git a/tdecore/tdehw/tdecryptographiccarddevice.cpp b/tdecore/tdehw/tdecryptographiccarddevice.cpp index 1fb1849ca..99c00b9e5 100644 --- a/tdecore/tdehw/tdecryptographiccarddevice.cpp +++ b/tdecore/tdehw/tdecryptographiccarddevice.cpp @@ -61,6 +61,8 @@ CryptoCardDeviceWatcher::CryptoCardDeviceWatcher() { #ifdef WITH_PCSC m_readerStates = NULL; #endif + m_cardPINPromptDone = true; + m_pinCallbacksEnabled = false; } CryptoCardDeviceWatcher::~CryptoCardDeviceWatcher() { @@ -189,6 +191,11 @@ void CryptoCardDeviceWatcher::requestTermination() { m_terminationRequested = true; } +void CryptoCardDeviceWatcher::setProvidedPin(TQString pin) { + m_cardPIN = pin; + m_cardPINPromptDone = true; +} + TQString CryptoCardDeviceWatcher::getCardATR(TQString readerName) { #ifdef WITH_PCSC unsigned int i; @@ -231,21 +238,54 @@ TQString CryptoCardDeviceWatcher::getCardATR(TQString readerName) { #endif } +void CryptoCardDeviceWatcher::enablePINEntryCallbacks(bool enable) { + m_pinCallbacksEnabled = enable; +} + +TQString CryptoCardDeviceWatcher::doPinRequest(TQString prompt) { + if (!m_pinCallbacksEnabled) { + return TQString::null; + } + + m_cardPINPromptDone = false; + emit(pinRequested(prompt)); + while (!m_cardPINPromptDone) { + usleep(100); + } + + if (m_cardPIN.length() > 0) { + return m_cardPIN; + } + else { + return TQString::null; + } +} + #ifdef WITH_PKCS static void pkcs_log_hook(IN void * const global_data, IN unsigned flags, IN const char * const format, IN va_list args) { vprintf(format, args); printf("\n"); } + +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()); + + // Success + return 1; + } + else { + // Abort + return 0; + } +} #endif -int CryptoCardDeviceWatcher::retrieveCardCertificates(TQString readerName) { +int CryptoCardDeviceWatcher::initializePkcs() { #if WITH_PKCS - int ret = -1; - CK_RV rv; - pkcs11h_certificate_id_list_t issuers; - pkcs11h_certificate_id_list_t certs; - printf("Initializing pkcs11-helper\n"); if ((rv = pkcs11h_initialize()) != CKR_OK) { printf("pkcs11h_initialize failed: %s\n", pkcs11h_getMessage(rv)); @@ -253,29 +293,51 @@ int CryptoCardDeviceWatcher::retrieveCardCertificates(TQString readerName) { } printf("Registering pkcs11-helper hooks\n"); - if ((rv = pkcs11h_setLogHook(pkcs_log_hook, NULL)) != CKR_OK) { + if ((rv = pkcs11h_setLogHook(pkcs_log_hook, this)) != CKR_OK) { printf("pkcs11h_setLogHook failed: %s\n", pkcs11h_getMessage(rv)); return -1; } pkcs11h_setLogLevel(PKCS11H_LOG_WARN); + // pkcs11h_setLogLevel(PKCS11H_LOG_DEBUG2); #if 0 if ((rv = pkcs11h_setTokenPromptHook(_pkcs11h_hooks_token_prompt, NULL)) != CKR_OK) { printf("pkcs11h_setTokenPromptHook failed: %s\n", pkcs11h_getMessage(rv)); return -1; } - if ((rv = pkcs11h_setPINPromptHook(_pkcs11h_hooks_pin_prompt, NULL)) != CKR_OK) { +#endif + + if ((rv = pkcs11h_setPINPromptHook(pkcs_pin_hook, this)) != CKR_OK) { printf("pkcs11h_setPINPromptHook failed: %s\n", pkcs11h_getMessage(rv)); return -1; } -#endif + printf("Adding provider '%s'\n", OPENSC_PKCS11_PROVIDER_LIBRARY); if ((rv = pkcs11h_addProvider(OPENSC_PKCS11_PROVIDER_LIBRARY, OPENSC_PKCS11_PROVIDER_LIBRARY, FALSE, PKCS11H_PRIVATEMODE_MASK_AUTO, PKCS11H_SLOTEVENT_METHOD_AUTO, 0, FALSE)) != CKR_OK) { printf("pkcs11h_addProvider failed: %s\n", pkcs11h_getMessage(rv)); return -1; } - rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_NONE, &issuers, &certs); + return 0; +#else + return -1; +#endif +} + +int CryptoCardDeviceWatcher::retrieveCardCertificates(TQString readerName) { +#if WITH_PKCS + int ret = -1; + + CK_RV rv; + pkcs11h_certificate_id_list_t issuers; + pkcs11h_certificate_id_list_t certs; + + if (initializePkcs() < 0) { + printf("Unable to initialize PKCS\n"); + return -1; + } + + rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, &issuers, &certs); if ((rv != CKR_OK) || (certs == NULL)) { printf("Cannot enumerate certificates: %s\n", pkcs11h_getMessage(rv)); return -1; @@ -288,13 +350,14 @@ 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_NONE, PKCS11H_PIN_CACHE_INFINITE, &certificate); + rv = pkcs11h_certificate_create(certs->certificate_id, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &certificate); if (rv != CKR_OK) { - printf("Can not read certificate: %s\n", pkcs11h_getMessage(rv)); + printf("Cannot read certificate: %s\n", pkcs11h_getMessage(rv)); pkcs11h_certificate_freeCertificateId(certs->certificate_id); ret = -1; break; } + pkcs11h_certificate_freeCertificateId(certs->certificate_id); pkcs11h_openssl_session_t openssl_session = NULL; @@ -335,13 +398,12 @@ int CryptoCardDeviceWatcher::retrieveCardCertificates(TQString readerName) { printf("Unable to copy X509 certificate\n"); } - pkcs11h_certificate_freeCertificateIdList(issuers); - pkcs11h_openssl_freeSession(openssl_session); - i++; } + pkcs11h_certificate_freeCertificateIdList(issuers); + return ret; #else return -1; @@ -386,6 +448,7 @@ void TDECryptographicCardDevice::enableCardMonitoring(bool enable) { m_watcherObject->cardDevice = this; m_watcherObject->moveToThread(m_watcherThread); TQObject::connect(m_watcherObject, SIGNAL(statusChanged(TQString,TQString)), this, SLOT(cardStatusChanged(TQString,TQString))); + TQObject::connect(m_watcherObject, SIGNAL(pinRequested(TQString)), this, SLOT(workerRequestedPin(TQString))); TQTimer::singleShot(0, m_watcherObject, SLOT(run())); m_watcherThread->start(); @@ -407,6 +470,12 @@ void TDECryptographicCardDevice::enableCardMonitoring(bool enable) { #endif } +void TDECryptographicCardDevice::enablePINEntryCallbacks(bool enable) { + if (m_watcherObject) { + m_watcherObject->enablePINEntryCallbacks(enable); + } +} + int TDECryptographicCardDevice::cardPresent() { if (m_watcherObject && m_watcherThread) { if (m_cardPresent) @@ -462,6 +531,134 @@ void TDECryptographicCardDevice::cardStatusChanged(TQString status, TQString atr } } +void TDECryptographicCardDevice::setProvidedPin(TQString pin) { + if (m_watcherObject) { + m_watcherObject->setProvidedPin(pin); + } +} + +void TDECryptographicCardDevice::workerRequestedPin(TQString prompt) { + emit(pinRequested(prompt, this)); +} + +int TDECryptographicCardDevice::decryptDataEncryptedWithCertPublicKey(TQByteArray &ciphertext, TQByteArray &plaintext, TQString *errstr) { +#if WITH_PKCS + int ret = -1; + + if (!m_watcherObject) { + if (errstr) *errstr = i18n("Card watcher object not available"); + return -1; + } + + CK_RV rv; + pkcs11h_certificate_id_list_t issuers; + pkcs11h_certificate_id_list_t certs; + + if (m_watcherObject->initializePkcs() < 0) { + if (errstr) *errstr = i18n("Unable to initialize PKCS"); + return -1; + } + + rv = pkcs11h_certificate_enumCertificateIds(PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, &issuers, &certs); + if ((rv != CKR_OK) || (certs == NULL)) { + if (errstr) *errstr = i18n("Cannot enumerate certificates: %1").arg(pkcs11h_getMessage(rv)); + return -1; + } + + int i = 0; + for (pkcs11h_certificate_id_list_t cert = certs; cert != NULL; cert = cert->next) { + 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); + if (rv != CKR_OK) { + if (errstr) *errstr = i18n("Cannot read certificate: %1").arg(pkcs11h_getMessage(rv)); + pkcs11h_certificate_freeCertificateId(certs->certificate_id); + ret = -1; + break; + } + + pkcs11h_certificate_freeCertificateId(certs->certificate_id); + + pkcs11h_openssl_session_t openssl_session = NULL; + if ((openssl_session = pkcs11h_openssl_createSession(certificate)) == NULL) { + if (errstr) *errstr = i18n("Cannot initialize openssl session to retrieve cryptographic objects"); + pkcs11h_certificate_freeCertificate(certificate); + ret = -1; + 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"); + ret = -1; + } + + // Extract public key from X509 certificate + EVP_PKEY* x509_pubkey = NULL; + RSA* rsa_pubkey = NULL; + x509_pubkey = X509_get_pubkey(x509_local); + if (x509_pubkey) { + 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")); + ret = -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").arg(pkcs11h_getMessage(rv)); + ret = -2; + } + 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)); + ret = -2; + } + else { + if (errstr) *errstr = TQString::null; + ret = 0; + } + } + + pkcs11h_openssl_freeSession(openssl_session); + + // Only interested in first certificate for now + // FIXME + // If cards with multiple certificates are used this should be modified to try decryption + // using each certificate in turn... + break; + + i++; + } + pkcs11h_certificate_freeCertificateIdList(issuers); + + return ret; +#else + return -1; +#endif +} + int TDECryptographicCardDevice::createNewSecretRSAKeyFromCertificate(TQByteArray &plaintext, TQByteArray &ciphertext, X509* certificate) { unsigned int i; int retcode = -1; diff --git a/tdecore/tdehw/tdecryptographiccarddevice.h b/tdecore/tdehw/tdecryptographiccarddevice.h index c9de6091b..fd5256d23 100644 --- a/tdecore/tdehw/tdecryptographiccarddevice.h +++ b/tdecore/tdehw/tdecryptographiccarddevice.h @@ -57,6 +57,20 @@ class TDECORE_EXPORT TDECryptographicCardDevice : public TDEGenericDevice */ void enableCardMonitoring(bool enable); + /** + * Enable / disable PIN entry. + * + * @note You must connect to pinRequested and call setProvidedPin with + * the provided PIN, otherwise the TDECryptographicCardDevice object + * will hang waiting for input. + * + * @param enable true to enable, false to disable. + * + * @see setProvidedPin(TQString pin) + * @see pinRequested + */ + void enablePINEntryCallbacks(bool enable); + /** * If monitoring of insert / remove events is enabled, * return whether or not a card is present. @@ -82,6 +96,27 @@ class TDECORE_EXPORT TDECryptographicCardDevice : public TDEGenericDevice */ X509CertificatePtrList cardX509Certificates(); + /** + * Sets the user-provided PIN from within the pinRequested callback. + * This method must not be called from anywhere else in user code. + * @param pin the user-provided PIN, TQString::null to abort + * + * @see pinRequested(TQString prompt) + */ + void setProvidedPin(TQString pin); + + /** + * 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. + * @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 + */ + int decryptDataEncryptedWithCertPublicKey(TQByteArray &ciphertext, TQByteArray &plaintext, TQString *errstr=NULL); + /** * Create a new random key and encrypt with the public key * contained in the given certificate. @@ -94,10 +129,12 @@ class TDECORE_EXPORT TDECryptographicCardDevice : public TDEGenericDevice public slots: void cardStatusChanged(TQString status, TQString atr); + void workerRequestedPin(TQString prompt); signals: void cardInserted(); void cardRemoved(); + void pinRequested(TQString prompt, TDECryptographicCardDevice* cdevice); private: TQEventLoopThread *m_watcherThread; diff --git a/tdecore/tdehw/tdecryptographiccarddevice_private.h b/tdecore/tdehw/tdecryptographiccarddevice_private.h index 4e6a588c4..3371098f2 100644 --- a/tdecore/tdehw/tdecryptographiccarddevice_private.h +++ b/tdecore/tdehw/tdecryptographiccarddevice_private.h @@ -53,6 +53,13 @@ class CryptoCardDeviceWatcher : public TQObject signals: void statusChanged(TQString, TQString); + void pinRequested(TQString); + + public: + int initializePkcs(); + TQString doPinRequest(TQString prompt); + void setProvidedPin(TQString pin); + void enablePINEntryCallbacks(bool enable); public: TDECryptographicCardDevice *cardDevice; @@ -62,6 +69,9 @@ class CryptoCardDeviceWatcher : public TQObject private: bool m_terminationRequested; + bool m_pinCallbacksEnabled; + TQString m_cardPIN; + bool m_cardPINPromptDone; #ifdef WITH_PCSC SCARDCONTEXT m_cardContext; SCARD_READERSTATE *m_readerStates; -- cgit v1.2.1