diff --git a/client/Application.cpp b/client/Application.cpp index c1fe2a90a..0a73e1d6c 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -305,6 +305,10 @@ class Application::Private #ifdef Q_OS_WIN QStringList tempFiles; #endif // Q_OS_WIN + + ~Private() { + delete signer; + } }; Application::Application( int &argc, char **argv ) @@ -428,6 +432,7 @@ Application::Application( int &argc, char **argv ) // Clear obsolete registriy settings #ifndef Q_OS_DARWIN Settings::DEFAULT_DIR.clear(); + Settings::CDOC2_NOTIFICATION.clear(); #endif // Actions diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 47a4419d1..5fc485072 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -93,11 +93,11 @@ CDocSupport::getCDocFileList(const QString &filename) } static libcdoc::result_t -getDecryptStatus(const std::vector& result, QCryptoBackend::PinStatus pin_status) +getDecryptStatus(QCryptoBackend::Status pin_status) { switch (pin_status) { case QCryptoBackend::PinOK: - return (result.empty()) ? DDCryptoBackend::BACKEND_ERROR : libcdoc::OK; + return libcdoc::OK; case QCryptoBackend::PinCanceled: return DDCryptoBackend::PIN_CANCELED; case QCryptoBackend::PinIncorrect: @@ -110,43 +110,55 @@ getDecryptStatus(const std::vector& result, QCryptoBackend::PinStatus p } libcdoc::result_t -DDCryptoBackend::decryptRSA(std::vector& result, const std::vector &data, bool oaep, unsigned int idx) +DDCryptoBackend::decryptRSA(std::vector& dst, const std::vector &data, bool oaep, unsigned int idx) { - QCryptoBackend::PinStatus pin_status; - QByteArray qkek = qApp->signer()->decrypt([qdata = toByteArray(data), &oaep](QCryptoBackend *backend) { - return backend->decrypt(qdata, oaep); - }, pin_status); - result.assign(qkek.cbegin(), qkek.cend()); - return getDecryptStatus(result, pin_status); + if (!backend) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + backend.reset(val.value()); + } + QByteArray decryptedKey = backend->decrypt(toByteArray(data), oaep); + dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); + backend.reset(); + return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); } libcdoc::result_t DDCryptoBackend::deriveConcatKDF(std::vector& dst, const std::vector &publicKey, const std::string &digest, const std::vector &algorithmID, const std::vector &partyUInfo, const std::vector &partyVInfo, unsigned int idx) { - QCryptoBackend::PinStatus pin_status; - QByteArray decryptedKey = qApp->signer()->decrypt([&publicKey, &digest, &algorithmID, &partyUInfo, &partyVInfo](QCryptoBackend *backend) { - static const QHash SHA_MTH{ - {"http://www.w3.org/2001/04/xmlenc#sha256", QCryptographicHash::Sha256}, - {"http://www.w3.org/2001/04/xmlenc#sha384", QCryptographicHash::Sha384}, - {"http://www.w3.org/2001/04/xmlenc#sha512", QCryptographicHash::Sha512} - }; - return backend->deriveConcatKDF(toByteArray(publicKey), SHA_MTH.value(digest), - toByteArray(algorithmID), toByteArray(partyUInfo), toByteArray(partyVInfo)); - }, pin_status); + static const QHash SHA_MTH{ + {"http://www.w3.org/2001/04/xmlenc#sha256", QCryptographicHash::Sha256}, + {"http://www.w3.org/2001/04/xmlenc#sha384", QCryptographicHash::Sha384}, + {"http://www.w3.org/2001/04/xmlenc#sha512", QCryptographicHash::Sha512} + }; + if (!backend) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + backend.reset(val.value()); + } + QByteArray decryptedKey = backend->deriveConcatKDF(toByteArray(publicKey), SHA_MTH.value(digest), + toByteArray(algorithmID), toByteArray(partyUInfo), toByteArray(partyVInfo)); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); - return getDecryptStatus(dst, pin_status); + backend.reset(); + return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); } libcdoc::result_t DDCryptoBackend::deriveHMACExtract(std::vector& dst, const std::vector &key_material, const std::vector &salt, unsigned int idx) { - QCryptoBackend::PinStatus pin_status; - QByteArray qkekpm = qApp->signer()->decrypt([qkey_material = toByteArray(key_material), qsalt = toByteArray(salt)](QCryptoBackend *backend) { - return backend->deriveHMACExtract(qkey_material, qsalt, ECC_KEY_LEN); - }, pin_status); - dst = std::vector(qkekpm.cbegin(), qkekpm.cend()); - return getDecryptStatus(dst, pin_status); + if (!backend) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + backend.reset(val.value()); + } + QByteArray decryptedKey = backend->deriveHMACExtract(toByteArray(key_material), toByteArray(salt), ECC_KEY_LEN); + dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); + backend.reset(); + return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); } libcdoc::result_t @@ -281,22 +293,25 @@ libcdoc::result_t DDNetworkBackend::sendKey( }; libcdoc::result_t -DDNetworkBackend::fetchKey(std::vector &result, - const std::string &url, - const std::string &transaction_id) { +DDNetworkBackend::fetchKey(std::vector &result, const std::string &url, const std::string &transaction_id) +{ QNetworkRequest req(QStringLiteral("%1/key-capsules/%2").arg(QString::fromStdString(url), QLatin1String(transaction_id.c_str()))); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); if(!checkConnection()) { last_error = "No connection"; return BACKEND_ERROR; } - QCryptoBackend::PinStatus pin_status; - auto authKey = dispatchToMain([&] { - return qApp->signer()->key(pin_status); - }); + + TokenData auth = qApp->signer()->tokenauth(); + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + std::unique_ptr backend(val.value()); + + auto authKey = backend->getKey(); if (!authKey.handle()) { - last_error = qApp->signer()->getLastErrorStr().toStdString(); - return getDecryptStatus(result, pin_status); + last_error = "Cannot create authentication key"; + return BACKEND_ERROR; } QScopedPointer nam( CheckConnection::setupNAM(req, qApp->signer()->tokenauth().cert(), authKey, Settings::CDOC2_GET_CERT)); @@ -304,9 +319,6 @@ DDNetworkBackend::fetchKey(std::vector &result, QNetworkReply *reply = nam->get(req); connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); e.exec(); - if(authKey.handle()) { - qApp->signer()->logout(); - } if(reply->error() != QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 201) { last_error = reply->errorString().toStdString(); @@ -315,6 +327,9 @@ DDNetworkBackend::fetchKey(std::vector &result, QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object(); QByteArray key_material = QByteArray::fromBase64(json.value(QLatin1String("ephemeral_key_material")).toString().toLatin1()); result.assign(key_material.cbegin(), key_material.cend()); + + crypto.setBackend(std::move(backend)); + return libcdoc::OK; } diff --git a/client/CDocSupport.h b/client/CDocSupport.h index b3c87bd90..42f6efc12 100644 --- a/client/CDocSupport.h +++ b/client/CDocSupport.h @@ -19,6 +19,8 @@ #pragma once +#include "QCryptoBackend.h" + #include #include #include @@ -76,9 +78,14 @@ struct DDCryptoBackend final : public libcdoc::CryptoBackend { unsigned int idx) final; std::string getLastErrorStr(libcdoc::result_t code) const final; + std::unique_ptr backend; std::vector secret; explicit DDCryptoBackend() = default; + + void setBackend(std::unique_ptr &&backend) { + this->backend = std::move(backend); + } }; // @@ -110,8 +117,9 @@ struct DDNetworkBackend final : public libcdoc::NetworkBackend, private QObject return libcdoc::NOT_IMPLEMENTED; } - explicit DDNetworkBackend() = default; + explicit DDNetworkBackend(DDCryptoBackend &_crypto) : crypto(_crypto) {} + DDCryptoBackend &crypto; std::string last_error; }; diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index d8de3b09c..ba9b1a477 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -84,6 +84,7 @@ struct CryptoDoc::Private std::vector files; std::vector keys; + explicit Private() : network(crypto) {} bool isEncryptedWarning(const QString &title) const; bool isEncrypted() const { @@ -340,28 +341,6 @@ bool CryptoDoc::decrypt(const libcdoc::Lock *lock, const QByteArray& secret) return false; } - if (d->reader->version == 2 && - (lock->type == libcdoc::Lock::Type::SERVER) && - !Settings::CDOC2_NOTIFICATION.isSet()) { - auto *dlg = WarningDialog::create() - ->withTitle(tr("You must enter your PIN code twice in order to decrypt the CDOC2 container")) - ->withText(tr( - "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " - "Second PIN entry is required to decrypt the CDOC2 container.")) - ->setCancelText(WarningDialog::Cancel) - ->addButton(WarningDialog::OK, QMessageBox::Ok) - ->addButton(tr("Don't show again"), QMessageBox::Ignore); - switch (dlg->exec()) - { - case QMessageBox::Ok: break; - case QMessageBox::Ignore: - Settings::CDOC2_NOTIFICATION = true; - break; - default: - return false; - } - } - d->crypto.secret.assign(secret.cbegin(), secret.cend()); TempListConsumer cons; @@ -394,13 +373,13 @@ bool CryptoDoc::decrypt(const libcdoc::Lock *lock, const QByteArray& secret) str = tr("Cannot read file."); break; case DDCryptoBackend::PIN_CANCELED: - str = tr("PIN entry canceled"); + str = QCryptoBackend::errorString(QCryptoBackend::Status::PinCanceled); break; case DDCryptoBackend::PIN_INCORRECT: - str = tr("PIN incorrect"); + str = QCryptoBackend::errorString(QCryptoBackend::Status::PinIncorrect); break; case DDCryptoBackend::PIN_LOCKED: - str = tr("PIN locked"); + QCryptoBackend::errorString(QCryptoBackend::Status::PinLocked); break; default: str = tr("Please check your internet connection and network settings."); diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 7107299e5..90ee32437 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -41,25 +41,46 @@ struct SCOPE constexpr T* operator&() noexcept { return &d; } }; -class QCNG::Private +struct QCNG::Private { -public: - TokenData token; - QCNG::PinStatus err = QCNG::PinOK; + SCOPE prov; + SCOPE key; + bool pss; }; -QCNG::QCNG( QObject *parent ) - : QCryptoBackend(parent) - , d(new Private) -{} +QCNG::QCNG() +{ +} QCNG::~QCNG() { - delete d; +} + +QCNG::Status QCNG::login(const TokenData &token) +{ + std::unique_ptr p = std::make_unique(); + if(FAILED(NCryptOpenStorageProvider(&p->prov, LPCWSTR(token.data(u"provider"_s).toString().utf16()), 0))) + return DeviceError; + if(FAILED(NCryptOpenKey(p->prov, &p->key, LPWSTR(token.data(u"key"_s).toString().utf16()), + token.data(u"spec"_s).value(), 0))) + return DeviceError; + // https://docs.microsoft.com/en-us/archive/blogs/alejacma/smart-cards-pin-gets-cached + NCryptSetProperty(p->key, NCRYPT_PIN_PROPERTY, nullptr, 0, 0); + p->pss = token.data(u"PSS"_s).toBool(); + d.reset(p.release()); + return PinOK; +} + +void +QCNG::logout() +{ + d.reset(); } QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const { + if (!d) + return {}; return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) { BCRYPT_OAEP_PADDING_INFO padding {BCRYPT_SHA256_ALGORITHM, nullptr, 0}; PVOID paddingInfo = oaep ? &padding : nullptr; @@ -81,6 +102,8 @@ QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const template QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) const { + if (!d) + return {}; return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &derived) { BCRYPT_ECCKEY_BLOB oh { BCRYPT_ECDH_PUBLIC_P384_MAGIC, ULONG((publicKey.size() - 1) / 2) }; switch((publicKey.size() - 1) * 4) @@ -105,6 +128,8 @@ QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) const QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const { + if (!d) + return {}; return derive(publicKey, [&](NCRYPT_SECRET_HANDLE sharedSecret, QByteArray &derived) { std::array paramValues{ BCryptBuffer{ULONG(algorithmID.size()), KDF_ALGORITHMID, PBYTE(algorithmID.data())}, @@ -135,6 +160,8 @@ QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const { + if (!d) + return {}; return derive(publicKey, [&](NCRYPT_SECRET_HANDLE sharedSecret, QByteArray &derived) { std::array paramValues{ BCryptBuffer{ULONG(salt.size()), KDF_HMAC_KEY, PBYTE(salt.data())}, @@ -155,33 +182,22 @@ QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray template QByteArray QCNG::exec(F &&func) const { - d->err = UnknownError; - SCOPE prov; - if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(d->token.data(u"provider"_s).toString().utf16()), 0))) - return {}; - SCOPE key; - if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(d->token.data(u"key"_s).toString().utf16()), - d->token.data(u"spec"_s).value(), 0))) - return {}; - // https://docs.microsoft.com/en-us/archive/blogs/alejacma/smart-cards-pin-gets-cached - NCryptSetProperty(key, NCRYPT_PIN_PROPERTY, nullptr, 0, 0); + status = UnknownError; QByteArray result; - switch(func(prov, key, result)) + switch(func(d->prov, d->key, result)) { case ERROR_SUCCESS: - d->err = PinOK; + status = PinOK; return result; case SCARD_W_CANCELLED_BY_USER: case ERROR_CANCELLED: - d->err = PinCanceled; + status = PinCanceled; default: return {}; } } -QCNG::PinStatus QCNG::lastError() const { return d->err; } - -QList QCNG::tokens() const +QList QCNG::tokens() { QList result; auto prop = [](NCRYPT_HANDLE handle, LPCWSTR param) -> QByteArray { @@ -275,14 +291,10 @@ QList QCNG::tokens() const return result; } -QCNG::PinStatus QCNG::login(const TokenData &token) -{ - d->token = token; - return d->err = QCNG::PinOK; -} - QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const { + if (!d) + return {}; return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) { BCRYPT_PSS_PADDING_INFO rsaPSS { NCRYPT_SHA256_ALGORITHM, 32 }; switch(type) @@ -301,7 +313,7 @@ QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &dige bool isRSA = algo == QLatin1String("RSA"); DWORD padding {}; PVOID paddingInfo {}; - if(isRSA && d->token.data(u"PSS"_s).toBool()) + if(isRSA && d->pss) { padding = BCRYPT_PAD_PSS; paddingInfo = &rsaPSS; diff --git a/client/QCNG.h b/client/QCNG.h index 279860ab5..c79e5f155 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -20,33 +20,33 @@ #pragma once #include "QCryptoBackend.h" +#include "TokenData.h" #include #include class QCNG final: public QCryptoBackend { - Q_OBJECT public: - explicit QCNG(QObject *parent = nullptr); + explicit QCNG(); ~QCNG() final; - QList tokens() const final; + Status login(const TokenData &token) final; + void logout() final; + QByteArray decrypt(const QByteArray &data, bool oaep) const final; QByteArray deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const final; QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const final; - PinStatus lastError() const final; - PinStatus login(const TokenData &token) final; - void logout() final {} QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; + static QList tokens(); private: template QByteArray derive(const QByteArray &publicKey, F &&func) const; template QByteArray exec(F &&func) const; - class Private; - Private *d; + struct Private; + std::unique_ptr d; }; diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index 254ebe201..9d9b5de1c 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -19,16 +19,158 @@ #include "QCryptoBackend.h" -QString QCryptoBackend::errorString(PinStatus error) +#include "TokenData.h" +#ifdef Q_OS_WIN +#include "QCNG.h" +#endif +#include "QPKCS11.h" + +#include +#include +#include + +// TODO: Port everything to the new OpenSSL API +#define OPENSSL_SUPPRESS_DEPRECATED + +#include +#include +#include + +std::expected +QCryptoBackend::getBackend(const TokenData& token) { +#ifdef Q_OS_WIN + auto backend = std::make_unique(); +#else + auto backend = std::make_unique(); +#endif + backend.get()->cert = token.cert(); + Status status; + do { + status = backend->login(token); + } while (status == PinIncorrect); + if (status != PinOK) return std::unexpected(status); + return backend.release(); +} + +QList +QCryptoBackend::getTokens() +{ +#ifdef Q_OS_WIN + return QCNG::tokens(); +#else + return QPKCS11::tokens(); +#endif +} + +QString QCryptoBackend::errorString(Status error) { switch( error ) { case PinOK: return QString(); - case PinCanceled: return tr("PIN Canceled"); - case PinLocked: return tr("PIN locked"); - case PinIncorrect: return tr("PIN Incorrect"); - case GeneralError: return tr("PKCS11 general error"); - case DeviceError: return tr("PKCS11 device error"); - default: return tr("Unknown error"); + case PinCanceled: return QCoreApplication::translate("QCryptoBackend", "PIN entry canceled"); + case PinLocked: return QCoreApplication::translate("QCryptoBackend", "PIN locked"); + case PinIncorrect: return QCoreApplication::translate("QCryptoBackend", "PIN incorrect"); + case GeneralError: return QCoreApplication::translate("QCryptoBackend", "PKCS11 general error"); + case DeviceError: return QCoreApplication::translate("QCryptoBackend", "PKCS11 device error"); + default: return QCoreApplication::translate("QCryptoBackend", "Unknown error"); } } + +static int rsa_sign(int type, const unsigned char *m, unsigned int m_len, unsigned char *sigret, unsigned int *siglen, const RSA *rsa) +{ + auto *backend = (QCryptoBackend*) RSA_get_ex_data(rsa, 0); + QCryptographicHash::Algorithm algo = QCryptographicHash::Sha256; + switch(type) + { + case NID_sha224: algo = QCryptographicHash::Sha224; break; + case NID_sha256: algo = QCryptographicHash::Sha256; break; + case NID_sha384: algo = QCryptographicHash::Sha384; break; + case NID_sha512: algo = QCryptographicHash::Sha512; break; + } + QByteArray result = backend->sign(algo, QByteArray::fromRawData((const char*)m, int(m_len))); + if(result.isEmpty()) + return 0; + *siglen = (unsigned int)result.size(); + memcpy(sigret, result.constData(), size_t(result.size())); + return 1; +} + +static ECDSA_SIG* +ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM * /*inv*/, const BIGNUM * /*rp*/, EC_KEY *eckey) +{ + auto *backend = (QCryptoBackend*)EC_KEY_get_ex_data(eckey, 0); + QByteArray result = backend->sign(QCryptographicHash::Sha512, QByteArray::fromRawData((const char*)dgst, dgst_len)); + if(result.isEmpty()) + return nullptr; + QByteArray r = result.left(result.size()/2); + QByteArray s = result.right(result.size()/2); + ECDSA_SIG *sig = ECDSA_SIG_new(); + ECDSA_SIG_set0(sig, + BN_bin2bn((const unsigned char*)r.data(), int(r.size()), nullptr), + BN_bin2bn((const unsigned char*)s.data(), int(s.size()), nullptr)); + return sig; +} + +static RSA_METHOD *get_rsa_method(bool release = false) +{ + static RSA_METHOD *method = nullptr; + if (!method && !release) { + method = RSA_meth_dup(RSA_get_default_method()); + RSA_meth_set1_name(method, "QSmartCard"); + RSA_meth_set_sign(method, rsa_sign); + } else if (method && release) { + RSA_meth_free(method); + method = nullptr; + } + return method; +} + +static EC_KEY_METHOD *get_ec_method(bool release = false) +{ + static EC_KEY_METHOD *method = nullptr; + if(!method && !release) { + method = EC_KEY_METHOD_new(EC_KEY_get_default_method()); + using EC_KEY_sign = int (*)(int type, const unsigned char *dgst, int dlen, unsigned char *sig, + unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey); + using EC_KEY_sign_setup = int (*)(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp); + EC_KEY_sign sign = nullptr; + EC_KEY_sign_setup sign_setup = nullptr; + EC_KEY_METHOD_get_sign(method, &sign, &sign_setup, nullptr); + EC_KEY_METHOD_set_sign(method, sign, sign_setup, ecdsa_do_sign); + } else if (method && release) { + EC_KEY_METHOD_free(method); + method = nullptr; + } + return method; +} + +QSslKey +QCryptoBackend::getKey() const +{ + QSslKey key = cert.publicKey(); + if(!key.handle()) { + status = GeneralError; + return {}; + } + if(key.algorithm() == QSsl::Ec) + { + auto *ec = (EC_KEY*)key.handle(); + EC_KEY_set_method(ec, get_ec_method()); + EC_KEY_set_ex_data(ec, 0, (void *) this); + } + else + { + RSA *rsa = (RSA*)key.handle(); + RSA_set_method(rsa, get_rsa_method()); + RSA_set_ex_data(rsa, 0, (void *) this); + } + return key; +} + +void +QCryptoBackend::shutDown() +{ + get_rsa_method(true); + get_ec_method(true); +} + diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index 210826b96..a9cc5e13c 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -19,16 +19,17 @@ #pragma once -#include -#include +#include +#include + +#include class TokenData; -class QCryptoBackend: public QObject +class QCryptoBackend { - Q_OBJECT public: - enum PinStatus : quint8 + enum Status : quint8 { PinOK, PinCanceled, @@ -39,17 +40,50 @@ class QCryptoBackend: public QObject UnknownError }; - using QObject::QObject; + virtual ~QCryptoBackend() {}; - virtual QList tokens() const = 0; virtual QByteArray decrypt(const QByteArray &data, bool oaep) const = 0; virtual QByteArray deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const = 0; virtual QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const = 0; - virtual PinStatus lastError() const { return PinOK; } - virtual PinStatus login(const TokenData &cert) = 0; - virtual void logout() = 0; virtual QByteArray sign(QCryptographicHash::Algorithm method, const QByteArray &digest) const = 0; - static QString errorString( PinStatus error ); + /** + * @brief Get the SSL key for the certificate + * + * @return the Qt SSL key + */ + QSslKey getKey() const; + /** + * @brief Get a new Backend object and log in with the given token + * + * @param token the token to use + * @return the new backend object or an error code + */ + static std::expected getBackend(const TokenData& token); + /** + * @brief Shut down all backends + * + * This should be called when the application is about to exit. It releases all static data held by backend(s) (e.g. PKCS11 library) + */ + static void shutDown(); + + /** + * @brief The status of the last operation + */ + mutable Status status = PinOK; + + /** + * @brief Get a list of all available tokens + * + * @return list of all available tokens + */ + static QList getTokens(); + + static QString errorString(Status error); +protected: + virtual Status login(const TokenData &cert) = 0; + virtual void logout() = 0; + + QSslCertificate cert; }; diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index f8c127d45..431d667f0 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -17,7 +17,8 @@ * */ -#include "QPKCS11_p.h" +#include "QPKCS11.h" +#include "pkcs11.h" #include "Application.h" #include "CryptoDoc.h" @@ -29,6 +30,7 @@ #include #include +#include #include @@ -44,7 +46,90 @@ static QString toQString(const Container &c) return QString::fromLatin1((const char*)std::data(c), std::size(c)); } -QByteArray QPKCS11::Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const +struct QPKCS11Private +{ + static bool load(const QString &driver); + static void unload(); + static void logout(); + QByteArray attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const; + std::vector findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id = {}) const; + + QLibrary lib; + CK_FUNCTION_LIST_PTR f = nullptr; + bool isFinDriver = false; + CK_SESSION_HANDLE session = 0; + QByteArray id; + bool isPSS = false; +}; + +static QPKCS11Private priv; + +static QPKCS11Private *getPrivate() +{ + return &priv; +} + +bool +QPKCS11Private::load(const QString &driver) +{ + QPKCS11Private *d = getPrivate(); + if(d->lib.fileName() == driver && d->f) + return true; + qWarning() << "Loading:" << driver; + unload(); + d->lib.setFileName(driver); + if(auto l = CK_C_GetFunctionList(d->lib.resolve("C_GetFunctionList")); + !l || l(&d->f) != CKR_OK) + { + qWarning() << "Failed to resolve symbols" << d->lib.errorString(); + return false; + } + + CK_C_INITIALIZE_ARGS init_args { nullptr, nullptr, nullptr, nullptr, CKF_OS_LOCKING_OK, nullptr }; + CK_RV err = d->f->C_Initialize( &init_args ); + if( err != CKR_OK && err != CKR_CRYPTOKI_ALREADY_INITIALIZED ) + { + qWarning() << "Failed to initalize"; + return false; + } + + CK_INFO info{}; + d->f->C_GetInfo( &info ); + qWarning() + << QStringLiteral("%1 (%2.%3)").arg(toQString(info.manufacturerID)) + .arg(info.cryptokiVersion.major).arg(info.cryptokiVersion.minor) << '\n' + << QStringLiteral("%1 (%2.%3)").arg(toQString(info.libraryDescription)) + .arg(info.libraryVersion.major).arg(info.libraryVersion.minor) << '\n' + << "Flags:" << info.flags; + d->isFinDriver = toQString(info.libraryDescription).contains(QLatin1String("MPOLLUX"), Qt::CaseInsensitive); + return true; +} + +void +QPKCS11Private::unload() +{ + logout(); + QPKCS11Private *d = getPrivate(); + if(d->f) + d->f->C_Finalize(nullptr); + d->f = nullptr; + d->lib.unload(); +} + +void +QPKCS11Private::logout() +{ + QPKCS11Private *d = getPrivate(); + d->id.clear(); + if (d->f && d->session) { + d->f->C_Logout(d->session); + d->f->C_CloseSession(d->session); + } + d->session = 0; +} + +QByteArray +QPKCS11Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const { QByteArray data; CK_ATTRIBUTE attr { type, nullptr, 0 }; @@ -57,7 +142,8 @@ QByteArray QPKCS11::Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HAND return data; } -std::vector QPKCS11::Private::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id) const +std::vector +QPKCS11Private::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id) const { std::vector result; if(!f) @@ -82,48 +168,58 @@ std::vector QPKCS11::Private::findObject(CK_SESSION_HANDLE ses return result; } - - -QPKCS11::QPKCS11( QObject *parent ) - : QCryptoBackend(parent) - , d(new Private) +QPKCS11::QPKCS11() + : QCryptoBackend() { } QPKCS11::~QPKCS11() { - unload(); - delete d; + logout(); } -QByteArray QPKCS11::decrypt(const QByteArray &data, bool oaep) const +QByteArray +QPKCS11::decrypt(const QByteArray &data, bool oaep) const { - std::vector key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); - if(key.size() != 1) + QPKCS11Private *d = getPrivate(); + auto key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); + if(key.size() != 1){ + status = GeneralError; return {}; + } CK_RSA_PKCS_OAEP_PARAMS params { CKM_SHA256, CKG_MGF1_SHA256, 0, nullptr, 0 }; auto mech = oaep ? CK_MECHANISM{ CKM_RSA_PKCS_OAEP, ¶ms, sizeof(params) } : CK_MECHANISM{ CKM_RSA_PKCS, nullptr, 0 }; - if(d->f->C_DecryptInit(d->session, &mech, key.front()) != CKR_OK) + if(d->f->C_DecryptInit(d->session, &mech, key.front()) != CKR_OK) { + status = GeneralError; return {}; + } CK_ULONG size = 0; - if(d->f->C_Decrypt(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), nullptr, &size) != CKR_OK) + if(d->f->C_Decrypt(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), nullptr, &size) != CKR_OK) { + status = GeneralError; return {}; - + } QByteArray result(int(size), 0); - if(d->f->C_Decrypt(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), CK_BYTE_PTR(result.data()), &size) != CKR_OK) + if(d->f->C_Decrypt(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), CK_BYTE_PTR(result.data()), &size) != CKR_OK) { + status = GeneralError; return {}; + } + return result; } -QByteArray QPKCS11::derive(const QByteArray &publicKey) const +QByteArray +QPKCS11::derive(const QByteArray &publicKey) const { + QPKCS11Private *d = getPrivate(); std::vector key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); - if(key.size() != 1) + if(key.size() != 1) { + status = GeneralError; return {}; + } CK_ECDH1_DERIVE_PARAMS ecdh_parms { CKD_NULL, 0, nullptr, CK_ULONG(publicKey.size()), CK_BYTE_PTR(publicKey.data()) }; CK_MECHANISM mech { CKM_ECDH1_DERIVE, &ecdh_parms, sizeof(CK_ECDH1_DERIVE_PARAMS) }; @@ -141,13 +237,16 @@ QByteArray QPKCS11::derive(const QByteArray &publicKey) const CK_ATTRIBUTE{CKA_VALUE_LEN, &value_len, sizeof(value_len)}, }; CK_OBJECT_HANDLE newkey = CK_INVALID_HANDLE; - if(d->f->C_DeriveKey(d->session, &mech, key.front(), newkey_template.data(), CK_ULONG(newkey_template.size()), &newkey) != CKR_OK) + if(d->f->C_DeriveKey(d->session, &mech, key.front(), newkey_template.data(), CK_ULONG(newkey_template.size()), &newkey) != CKR_OK) { + status = GeneralError; return {}; + } return d->attribute(d->session, newkey, CKA_VALUE); } -QByteArray QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, +QByteArray +QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const { QByteArray z = derive(publicKey); @@ -171,7 +270,8 @@ QByteArray QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicH return key.left(int(keyDataLen)); } -QByteArray QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const +QByteArray +QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const { QByteArray key = derive(publicKey); if(key.isEmpty()) @@ -179,12 +279,13 @@ QByteArray QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteAr auto ctx = libcdoc::make_unique_ptr(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); QByteArray out(keySize, 0); auto outlen = size_t(out.length()); - auto isError = [](int err) { + auto isError = [this](int err) { if(err < 1) { unsigned long errorCode = 0; while((errorCode = ERR_get_error())) qCWarning(CRYPTO) << ERR_error_string(errorCode, nullptr); + status = GeneralError; } return err < 1; }; @@ -199,49 +300,12 @@ QByteArray QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteAr return out; } -bool QPKCS11::isLoaded() const -{ - return d->f != nullptr; -} - -bool QPKCS11::load( const QString &driver ) -{ - if(d->lib.fileName() == driver && isLoaded()) - return true; - qWarning() << "Loading:" << driver; - unload(); - d->lib.setFileName( driver ); - if(auto l = CK_C_GetFunctionList(d->lib.resolve("C_GetFunctionList")); - !l || l(&d->f) != CKR_OK) - { - qWarning() << "Failed to resolve symbols" << d->lib.errorString(); - return false; - } - - CK_C_INITIALIZE_ARGS init_args { nullptr, nullptr, nullptr, nullptr, CKF_OS_LOCKING_OK, nullptr }; - CK_RV err = d->f->C_Initialize( &init_args ); - if( err != CKR_OK && err != CKR_CRYPTOKI_ALREADY_INITIALIZED ) - { - qWarning() << "Failed to initalize"; - return false; - } - - CK_INFO info{}; - d->f->C_GetInfo( &info ); - qWarning() - << QStringLiteral("%1 (%2.%3)").arg(toQString(info.manufacturerID)) - .arg(info.cryptokiVersion.major).arg(info.cryptokiVersion.minor) << '\n' - << QStringLiteral("%1 (%2.%3)").arg(toQString(info.libraryDescription)) - .arg(info.libraryVersion.major).arg(info.libraryVersion.minor) << '\n' - << "Flags:" << info.flags; - d->isFinDriver = toQString(info.libraryDescription).contains(QLatin1String("MPOLLUX"), Qt::CaseInsensitive); - return true; -} - -QPKCS11::PinStatus QPKCS11::login(const TokenData &t) +QPKCS11::Status +QPKCS11::login(const TokenData &t) { logout(); + QPKCS11Private *d = getPrivate(); auto currentSlot = t.data(QStringLiteral("slot")).value(); d->id = t.data(QStringLiteral("id")).toByteArray(); d->isPSS = t.data(QStringLiteral("PSS")).toBool(); @@ -282,36 +346,39 @@ QPKCS11::PinStatus QPKCS11::login(const TokenData &t) } }); - switch( err ) + switch(err) { case CKR_OK: - case CKR_USER_ALREADY_LOGGED_IN: return PinOK; + case CKR_USER_ALREADY_LOGGED_IN: + return PinOK; case CKR_CANCEL: - case CKR_FUNCTION_CANCELED: return PinCanceled; + case CKR_FUNCTION_CANCELED: + return PinCanceled; case CKR_PIN_INCORRECT: d->f->C_GetTokenInfo(currentSlot, &token); return (token.flags & CKF_USER_PIN_LOCKED) ? PinLocked : PinIncorrect; - case CKR_PIN_LOCKED: return PinLocked; - case CKR_DEVICE_ERROR: return DeviceError; - case CKR_GENERAL_ERROR: return GeneralError; - default: return UnknownError; + case CKR_PIN_LOCKED: + return PinLocked; + case CKR_DEVICE_ERROR: + return DeviceError; + case CKR_GENERAL_ERROR: + return GeneralError; + default: + return UnknownError; } } void QPKCS11::logout() { - d->id.clear(); - if( d->f && d->session ) - { - d->f->C_Logout( d->session ); - d->f->C_CloseSession( d->session ); - } - d->session = 0; + QPKCS11Private *d = getPrivate(); + d->logout(); } -QList QPKCS11::tokens() const +QList QPKCS11::tokens() { QList list; + reload(); + QPKCS11Private *d = getPrivate(); if(!d->f) return list; size_t size = 0; @@ -423,18 +490,21 @@ bool QPKCS11::reload() continue; QByteArray atr = r.atr(); for(auto i = drivers.cbegin(); i != drivers.cend(); ++i) { - if(i.value() == atr && load(i.key())) + if(i.value() == atr && QPKCS11Private::load(i.key())) return true; } } - return load(drivers.key({})); + return QPKCS11Private::load(drivers.key({})); } QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const { + QPKCS11Private *d = getPrivate(); std::vector key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); - if(key.size() != 1) + if(key.size() != 1) { + status = GeneralError; return {}; + } CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE attribute { CKA_KEY_TYPE, &keyType, sizeof(keyType) }; @@ -473,22 +543,19 @@ QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &d } data.append(digest); - if(d->f->C_SignInit(d->session, &mech, key.front()) != CKR_OK) + if(d->f->C_SignInit(d->session, &mech, key.front()) != CKR_OK) { + status = GeneralError; return {}; + } CK_ULONG size = 0; - if(d->f->C_Sign(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), nullptr, &size) != CKR_OK) + if(d->f->C_Sign(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), nullptr, &size) != CKR_OK) { + status = GeneralError; return {}; + } QByteArray sig(int(size), 0); - if(d->f->C_Sign(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), CK_BYTE_PTR(sig.data()), &size) != CKR_OK) + if(d->f->C_Sign(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), CK_BYTE_PTR(sig.data()), &size) != CKR_OK) { + status = GeneralError; return {}; + } return sig; } - -void QPKCS11::unload() -{ - logout(); - if(d->f) - d->f->C_Finalize(nullptr); - d->f = nullptr; - d->lib.unload(); -} diff --git a/client/QPKCS11.h b/client/QPKCS11.h index 468e7e23b..fbc80e10b 100644 --- a/client/QPKCS11.h +++ b/client/QPKCS11.h @@ -23,9 +23,8 @@ class QPKCS11 final: public QCryptoBackend { - Q_OBJECT public: - explicit QPKCS11(QObject *parent = nullptr); + explicit QPKCS11(); ~QPKCS11() final; QByteArray decrypt(const QByteArray &data, bool oaep) const final; @@ -33,15 +32,11 @@ class QPKCS11 final: public QCryptoBackend QByteArray deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const final; QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const final; - bool isLoaded() const; - bool load( const QString &driver ); - void unload(); - PinStatus login(const TokenData &t) final; - void logout() final; - bool reload(); QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; - QList tokens() const final; -private: - class Private; - Private *d; + + Status login(const TokenData &t) final; + void logout() final; + static bool reload(); + + static QList tokens(); }; diff --git a/client/QPKCS11_p.h b/client/QPKCS11_p.h deleted file mode 100644 index c80e6b21f..000000000 --- a/client/QPKCS11_p.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include "QPKCS11.h" - -#include "pkcs11.h" - -#include - -#include - -class QPKCS11::Private: public QObject -{ - Q_OBJECT -public: - QByteArray attribute( CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type ) const; - std::vector findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id = {}) const; - - QLibrary lib; - CK_FUNCTION_LIST_PTR f = nullptr; - bool isFinDriver = false; - CK_SESSION_HANDLE session = 0; - QByteArray id; - bool isPSS = false; -}; diff --git a/client/QSigner.cpp b/client/QSigner.cpp index e8328e74a..790ce23e4 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -49,77 +49,20 @@ static Q_LOGGING_CATEGORY(SLog, "qdigidoc4.QSigner") class QSigner::Private final { public: - QCryptoBackend *backend {}; QSmartCard *smartcard {}; TokenData auth, sign; QList cache; QReadWriteLock lock; QMutex sleepMutex; QWaitCondition sleepCond; - - static ECDSA_SIG* ecdsa_do_sign(const unsigned char *dgst, int dgst_len, - const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey); - static int rsa_sign(int type, const unsigned char *m, unsigned int m_len, - unsigned char *sigret, unsigned int *siglen, const RSA *rsa); - - RSA_METHOD *rsamethod = RSA_meth_dup(RSA_get_default_method()); - EC_KEY_METHOD *ecmethod = EC_KEY_METHOD_new(EC_KEY_get_default_method()); }; -ECDSA_SIG* QSigner::Private::ecdsa_do_sign(const unsigned char *dgst, int dgst_len, - const BIGNUM * /*inv*/, const BIGNUM * /*rp*/, EC_KEY *eckey) -{ - auto *backend = (QCryptoBackend*)EC_KEY_get_ex_data(eckey, 0); - QByteArray result = backend->sign(QCryptographicHash::Sha512, QByteArray::fromRawData((const char*)dgst, dgst_len)); - if(result.isEmpty()) - return nullptr; - QByteArray r = result.left(result.size()/2); - QByteArray s = result.right(result.size()/2); - ECDSA_SIG *sig = ECDSA_SIG_new(); - ECDSA_SIG_set0(sig, - BN_bin2bn((const unsigned char*)r.data(), int(r.size()), nullptr), - BN_bin2bn((const unsigned char*)s.data(), int(s.size()), nullptr)); - return sig; -} - -int QSigner::Private::rsa_sign(int type, const unsigned char *m, unsigned int m_len, - unsigned char *sigret, unsigned int *siglen, const RSA *rsa) -{ - auto *backend = (QCryptoBackend*)RSA_get_ex_data(rsa, 0); - QCryptographicHash::Algorithm algo = QCryptographicHash::Sha256; - switch(type) - { - case NID_sha224: algo = QCryptographicHash::Sha224; break; - case NID_sha256: algo = QCryptographicHash::Sha256; break; - case NID_sha384: algo = QCryptographicHash::Sha384; break; - case NID_sha512: algo = QCryptographicHash::Sha512; break; - } - QByteArray result = backend->sign(algo, QByteArray::fromRawData((const char*)m, int(m_len))); - if(result.isEmpty()) - return 0; - *siglen = (unsigned int)result.size(); - memcpy(sigret, result.constData(), size_t(result.size())); - return 1; -} - - - using namespace digidoc; QSigner::QSigner(QObject *parent) : QThread(parent) , d(new Private) { - RSA_meth_set1_name(d->rsamethod, "QSmartCard"); - RSA_meth_set_sign(d->rsamethod, Private::rsa_sign); - using EC_KEY_sign = int (*)(int type, const unsigned char *dgst, int dlen, unsigned char *sig, - unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey); - using EC_KEY_sign_setup = int (*)(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp); - EC_KEY_sign sign = nullptr; - EC_KEY_sign_setup sign_setup = nullptr; - EC_KEY_METHOD_get_sign(d->ecmethod, &sign, &sign_setup, nullptr); - EC_KEY_METHOD_set_sign(d->ecmethod, sign, sign_setup, Private::ecdsa_do_sign); - d->smartcard = new QSmartCard(parent); connect(this, &QSigner::error, this, [](const QString &title, const QString &msg) { WarningDialog::create() @@ -156,8 +99,6 @@ QSigner::~QSigner() d->sleepCond.wakeAll(); wait(); delete d->smartcard; - RSA_meth_free(d->rsamethod); - EC_KEY_METHOD_free(d->ecmethod); delete d; } @@ -171,108 +112,8 @@ X509Cert QSigner::cert() const return X509Cert((const unsigned char*)der.constData(), size_t(der.size()), X509Cert::Der); } -QByteArray QSigner::decrypt(std::function &&func, QCryptoBackend::PinStatus& pin_status) -{ - if(!d->lock.tryLockForWrite(10 * 1000)) - { - Q_EMIT error(tr("Failed to decrypt document"), tr("Signing/decrypting is already in progress another window.")); - pin_status = QCryptoBackend::GeneralError; - return {}; - } - - if( d->auth.cert().isNull() ) - { - Q_EMIT error(tr("Failed to decrypt document"), tr("Authentication certificate is not selected.")); - d->lock.unlock(); - pin_status = QCryptoBackend::GeneralError; - return {}; - } - - switch(pin_status = QCryptoBackend::PinStatus(login(d->auth))) - { - case QCryptoBackend::PinOK: break; - case QCryptoBackend::PinCanceled: return {}; - case QCryptoBackend::PinLocked: - Q_EMIT error(tr("Failed to decrypt document"), QCryptoBackend::errorString(pin_status)); - return {}; - default: - Q_EMIT error(tr("Failed to decrypt document"), tr("Failed to login token") + ' ' + QCryptoBackend::errorString(pin_status)); - return {}; - } - QByteArray result = waitFor(func, d->backend); - logout(); - if(d->backend->lastError() == QCryptoBackend::PinCanceled) { - pin_status = QCryptoBackend::PinCanceled; - return {}; - } - - if(result.isEmpty()) - Q_EMIT error(tr("Failed to decrypt document"), {}); - return result; -} - -QSslKey QSigner::key(QCryptoBackend::PinStatus& pin_status) -{ - QSslKey key = d->auth.cert().publicKey(); - if(!key.handle()) { - pin_status = QCryptoBackend::GeneralError; - return {}; - } - if(!d->lock.tryLockForWrite(10 * 1000)) { - Q_EMIT error(tr("Failed to decrypt document"), tr("Signing/decrypting is already in progress another window.")); - pin_status = QCryptoBackend::GeneralError; - return {}; - } - switch(pin_status = QCryptoBackend::PinStatus(login(d->auth))) - { - case QCryptoBackend::PinOK: break; - case QCryptoBackend::PinCanceled: return {}; - case QCryptoBackend::PinLocked: - Q_EMIT error(tr("Failed to decrypt document"), QCryptoBackend::errorString(pin_status)); - return {}; - default: - Q_EMIT error(tr("Failed to decrypt document"), tr("Failed to login token") + ' ' + QCryptoBackend::errorString(pin_status)); - return {}; - } - if(key.algorithm() == QSsl::Ec) - { - auto *ec = (EC_KEY*)key.handle(); - EC_KEY_set_method(ec, d->ecmethod); - EC_KEY_set_ex_data(ec, 0, d->backend); - } - else - { - RSA *rsa = (RSA*)key.handle(); - RSA_set_method(rsa, d->rsamethod); - RSA_set_ex_data(rsa, 0, d->backend); - } - return key; -} - -quint8 QSigner::login(const TokenData &token) const -{ - switch(auto status = d->backend->login(token)) - { - case QCryptoBackend::PinOK: return status; - case QCryptoBackend::PinIncorrect: - dispatchToMain([&]{ - WarningDialog::create() - ->withTitle(SslCertificate(token.cert()).keyUsage().contains(SslCertificate::NonRepudiation) ? tr("Failed to sign document") : tr("Failed to decrypt document")) - ->withText(QCryptoBackend::errorString(status)) - ->exec(); - }); - return login(token); - default: - d->lock.unlock(); - // QSmartCard should also know that PIN is blocked. - d->smartcard->reloadCard(d->smartcard->tokenData(), true); - return status; - } -} - void QSigner::logout() const { - d->backend->logout(); d->lock.unlock(); // QSmartCard should also know that PIN1 info is updated d->smartcard->reloadCard(d->smartcard->tokenData(), true); @@ -303,25 +144,11 @@ void QSigner::run() { d->auth.clear(); d->sign.clear(); -#ifdef Q_OS_WIN - d->backend = new QCNG(this); -#else - d->backend = new QPKCS11(this); -#endif - - while(!isInterruptionRequested()) - { - if(d->lock.tryLockForRead()) - { - auto *pkcs11 = qobject_cast(d->backend); - if(pkcs11 && !pkcs11->reload()) - { - Q_EMIT error(tr("Failed to load PKCS#11 module"), {}); - return; - } + while(!isInterruptionRequested()) { + if(d->lock.tryLockForRead()) { QList acards, scards; - QList cache = d->backend->tokens(); + QList cache = QCryptoBackend::getTokens(); if(cache != d->cache) { d->cache = std::move(cache); @@ -374,6 +201,7 @@ void QSigner::run() break; d->sleepCond.wait(&d->sleepMutex, 5000); } + QCryptoBackend::shutDown(); } void QSigner::selectCard(const TokenData &token) @@ -415,23 +243,22 @@ std::vector QSigner::sign(const std::string &method, const std::v throwException(tr("Signing certificate is not selected."), Exception::General) } - switch(auto status = QCryptoBackend::PinStatus(login(d->sign))) - { - case QCryptoBackend::PinOK: break; - case QCryptoBackend::PinCanceled: - throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(status)), Exception::PINCanceled); - case QCryptoBackend::PinLocked: - throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(status)), Exception::PINLocked); - default: - throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(status)), Exception::PINFailed); + auto val = QCryptoBackend::getBackend(d->sign); + if (!val.value()) { + switch(val.error()) { + case QCryptoBackend::PinCanceled: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINCanceled); + case QCryptoBackend::PinLocked: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINLocked); + default: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINFailed); + } } - QByteArray sig = waitFor(&QCryptoBackend::sign, d->backend, - methodToNID(method), QByteArray::fromRawData((const char*)digest.data(), int(digest.size()))); - logout(); - if(d->backend->lastError() == QCryptoBackend::PinCanceled) - throwException(tr("Failed to login token"), Exception::PINCanceled) + std::unique_ptr backend(val.value()); - if( sig.isEmpty() ) + QByteArray sig = waitFor(&QCryptoBackend::sign, backend.get(), + methodToNID(method), QByteArray::fromRawData((const char*)digest.data(), int(digest.size()))); + if (sig.isEmpty()) throwException(tr("Failed to sign document"), Exception::General) return {sig.constBegin(), sig.constEnd()}; } @@ -443,6 +270,5 @@ TokenData QSigner::tokensign() const { return d->sign; } QString QSigner::getLastErrorStr() const { - QCryptoBackend::PinStatus status = d->backend->lastError(); - return d->backend->errorString(status); + return "Backend error"; } diff --git a/client/QSigner.h b/client/QSigner.h index 286320631..92cc91a6a 100644 --- a/client/QSigner.h +++ b/client/QSigner.h @@ -41,8 +41,6 @@ class QSigner final: public QThread, public digidoc::Signer QList cache() const; digidoc::X509Cert cert() const final; - QByteArray decrypt(std::function &&func, QCryptoBackend::PinStatus& pin_status); - QSslKey key(QCryptoBackend::PinStatus& pin_status); void logout() const; void selectCard(const TokenData &token); std::vector sign( const std::string &method, @@ -59,7 +57,6 @@ class QSigner final: public QThread, public digidoc::Signer void error(const QString &title, const QString &text); private: - quint8 login(const TokenData &token) const; static QCryptographicHash::Algorithm methodToNID(const std::string &method); void run() final; diff --git a/client/Settings.cpp b/client/Settings.cpp index 54a7743bc..a4a16bcb8 100644 --- a/client/Settings.cpp +++ b/client/Settings.cpp @@ -30,6 +30,7 @@ using Option = Settings::Option; const Option Settings::CDOC2_DEFAULT { QStringLiteral("CDOC2-DEFAULT"), [] { return Application::confValue(QLatin1String("CDOC2-DEFAULT")).toBool(false); }}; +// TODO: Remove this after 2 releases const Option Settings::CDOC2_NOTIFICATION { QStringLiteral("CDOC2-NOTIFICATION"), false }; const Option Settings::CDOC2_USE_KEYSERVER { QStringLiteral("CDOC2-USE-KEYSERVER"), [] { return Application::confValue(QLatin1String("CDOC2-USE-KEYSERVER")).toBool(true); diff --git a/client/Settings.h b/client/Settings.h index d55a03695..b5d0af5dc 100644 --- a/client/Settings.h +++ b/client/Settings.h @@ -75,6 +75,7 @@ struct Settings }; static const Option CDOC2_DEFAULT; + // TODO: Remove this after 2 releases static const Option CDOC2_NOTIFICATION; static const Option CDOC2_USE_KEYSERVER; static const Option CDOC2_DEFAULT_KEYSERVER; diff --git a/client/translations/en.ts b/client/translations/en.ts index 158c6a678..291fe505b 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -489,14 +489,6 @@ Decrypting Decrypting - - You are about to delete the last file in the container - You are about to delete the last file in the container - - - It is removed along with the container. - It is removed along with the container. - In order to view Validity Confirmation Sheet there has to be at least one printer installed! In order to view Validity Confirmation Sheet there has to be at least one printer installed! @@ -580,14 +572,6 @@ Failed to add key Failed to add key - - You must enter your PIN code twice in order to decrypt the CDOC2 container - You must enter your PIN code twice in order to decrypt the CDOC2 container - - - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - Please check your internet connection and network settings. Please check your internet connection and network settings. @@ -604,10 +588,6 @@ Failed to open document Failed to open document - - Don't show again - Don't show again - Wrong password. Wrong password. @@ -2124,18 +2104,10 @@ ID-Card QCryptoBackend - - PIN Canceled - PIN Canceled - PIN locked PIN locked - - PIN Incorrect - PIN Incorrect - PKCS11 general error PKCS11 general error @@ -2148,6 +2120,14 @@ ID-Card Unknown error Unknown error + + PIN incorrect + PIN Incorrect + + + PIN entry canceled + PIN entry canceled + QFileDialog @@ -2158,10 +2138,6 @@ ID-Card QSigner - - Failed to load PKCS#11 module - Failed to load PKCS#11 module - Signing certificate is not selected. Signing certificate is not selected. @@ -2182,10 +2158,6 @@ ID-Card Signing/decrypting is already in progress another window. Signing/decrypting is already in progress another window. - - Authentication certificate is not selected. - Authentication certificate is not selected. - Failed to decrypt document Failed to decrypt document @@ -3051,6 +3023,14 @@ Additional licenses and components Remove Remove + + You are about to delete the last file in the container + You are about to delete the last file in the container + + + It is removed along with the container. + It is removed along with the container. + WarningItem diff --git a/client/translations/et.ts b/client/translations/et.ts index a26b51075..f485df513 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -489,14 +489,6 @@ Decrypting Dekrüpteerin - - You are about to delete the last file in the container - Oled kustutamas viimast faili ümbrikus - - - It is removed along with the container. - Koos sellega eemaldatakse ka ümbrik. - In order to view Validity Confirmation Sheet there has to be at least one printer installed! Digitaalallkirjade kinnituslehe kuvamiseks peab olema arvutis vähemalt üks printer seadistatud! @@ -580,14 +572,6 @@ Failed to add key Võtme lisamine ebaõnnestus - - You must enter your PIN code twice in order to decrypt the CDOC2 container - CDOC2 ümbriku dekrüpteerimiseks tuleb sisestada PIN-koodi kaks korda - - - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - Esimene PIN-koodi sisestamine on vajalik autentimiseks CDOC2 ümbrikus viidatud võtmeedastusserverisse. Teine PIN-koodi sisestamine on vajalik CDOC2 ümbriku dekrüpteerimiseks. - Please check your internet connection and network settings. Palun kontrolli internetiühendust ja võrgu sätteid. @@ -604,10 +588,6 @@ Failed to open document Dokumendi avamine ebaõnnestus - - Don't show again - Ära rohkem näita - Wrong password. Vale parool. @@ -2124,18 +2104,10 @@ ID-kaardiga QCryptoBackend - - PIN Canceled - PIN katkestatud - PIN locked PIN on lukus - - PIN Incorrect - Vale PIN - PKCS11 general error PKCS11 üldine viga @@ -2148,6 +2120,14 @@ ID-kaardiga Unknown error Tundmatu viga + + PIN incorrect + Vale PIN + + + PIN entry canceled + PIN sisestus katkestatus + QFileDialog @@ -2158,10 +2138,6 @@ ID-kaardiga QSigner - - Failed to load PKCS#11 module - PKCS#11 mooduli laadimine ebaõnnestus - Signing certificate is not selected. Allkirjastamise sertifikaat ei ole valitud. @@ -2182,10 +2158,6 @@ ID-kaardiga Signing/decrypting is already in progress another window. Allkirjastamine/dekrüpteerimine on juba käimas teises aknas. - - Authentication certificate is not selected. - Isikutuvastamise sertifikaat ei ole valitud. - Failed to decrypt document Dokumendi dekrüpteerimine ebaõnnestus @@ -3051,6 +3023,14 @@ Täiendavad litsentsid ja komponendid Remove Eemalda + + You are about to delete the last file in the container + Oled kustutamas viimast faili ümbrikus + + + It is removed along with the container. + Koos sellega eemaldatakse ka ümbrik. + WarningItem diff --git a/client/translations/ru.ts b/client/translations/ru.ts index 0bf05445e..0d330f261 100644 --- a/client/translations/ru.ts +++ b/client/translations/ru.ts @@ -489,14 +489,6 @@ Decrypting Расшифровка - - You are about to delete the last file in the container - Вы собираетесь удалить последний файл в контейнере - - - It is removed along with the container. - Он удаляется вместе с контейнером. - In order to view Validity Confirmation Sheet there has to be at least one printer installed! Для отображения листа подтверждения действительности цифровой подписи на компьютере должен быть установлен как минимум один принтер! @@ -580,14 +572,6 @@ Failed to add key Не удалось добавить ключ - - You must enter your PIN code twice in order to decrypt the CDOC2 container - PIN-код необходимо ввести дважды, чтобы расшифровать конверт CDOC2 - - - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - Первый ввод PIN-кода требуется для аутентификации на сервере передачи ключей, указанном в конверте CDOC2. Для расшифровки конверта CDOC2 требуется второй ввод PIN-кода. - Please check your internet connection and network settings. Пожалуйста, проверьте подключение к Интернету и настройки сети. @@ -604,10 +588,6 @@ Failed to open document Не удалось открыть документ - - Don't show again - Больше не показывать - Wrong password. Неверный пароль. @@ -2124,18 +2104,10 @@ ID-картой QCryptoBackend - - PIN Canceled - PIN-код отменен - PIN locked PIN заблокирован - - PIN Incorrect - Неверный PIN - PKCS11 general error Ошибка PKCS11 @@ -2148,6 +2120,14 @@ ID-картой Unknown error Неизвестная ошибка + + PIN incorrect + Неверный PIN + + + PIN entry canceled + PIN-код отменен + QFileDialog @@ -2158,10 +2138,6 @@ ID-картой QSigner - - Failed to load PKCS#11 module - Неудачная загрузка модуля PKCS#11 - Signing certificate is not selected. Сертификат подписи не выбран. @@ -2182,10 +2158,6 @@ ID-картой Signing/decrypting is already in progress another window. Операция подписи/расшифровки уже выполняется в другом окне. - - Authentication certificate is not selected. - Сертификат идентификации не выбран. - Failed to decrypt document Не удалось расшифровать документ @@ -3052,6 +3024,14 @@ Additional licenses and components Remove Удалить + + You are about to delete the last file in the container + Вы собираетесь удалить последний файл в контейнере + + + It is removed along with the container. + Он удаляется вместе с контейнером. + WarningItem diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index e2c7aeb3a..18d3dab8b 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -232,7 +232,7 @@ void ContainerPage::deleteConfirm(C *c, int index) if (dlg->exec() != QMessageBox::Ok) return; window()->setWindowFilePath({}); - window()->setWindowTitle(tr("DigiDoc4 Client")); + window()->setWindowTitle(QCoreApplication::translate("MainWindow", "DigiDoc4 Client")); if(QFile::exists(c->fileName())) QFile::remove(c->fileName()); emit action(ContainerClose);