From 682f40dd92e628db12ba3401f505c09f31770915 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 17 Apr 2026 14:58:02 +0300 Subject: [PATCH 01/15] Removed decryption methods from QSigner, reworked QCryptBackend lifecycle --- client/CDocSupport.cpp | 71 +++++++----- client/QCryptoBackend.cpp | 176 ++++++++++++++++++++++++++++- client/QCryptoBackend.h | 20 +++- client/QPKCS11.cpp | 225 +++++++++++++++++++++++--------------- client/QPKCS11.h | 14 +-- client/QPKCS11_p.h | 41 ------- client/QSigner.cpp | 147 ++----------------------- client/QSigner.h | 2 - 8 files changed, 376 insertions(+), 320 deletions(-) delete mode 100644 client/QPKCS11_p.h diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 47a4419d1..9a94a0ffe 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -110,43 +110,52 @@ 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); + auto backend = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (backend->status != QCryptoBackend::PinOK) { + return getDecryptStatus({}, backend->status); + } + // fixme: Locking + // fixme: missing token + QByteArray decryptedKey = backend->decrypt(toByteArray(data), oaep); + dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); + return getDecryptStatus(dst, QCryptoBackend::PinOK); } 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} + }; + auto backend = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (backend->status != QCryptoBackend::PinOK) { + return getDecryptStatus({}, backend->status); + } + // fixme: Locking + // fixme: missing token + 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); + return getDecryptStatus(dst, QCryptoBackend::PinOK); } 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); + auto backend = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (backend->status != QCryptoBackend::PinOK) { + return getDecryptStatus({}, backend->status); + } + // fixme: Locking + // fixme: missing token + QByteArray decryptedKey = backend->deriveHMACExtract(toByteArray(key_material), toByteArray(salt), ECC_KEY_LEN); + dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); + return getDecryptStatus(dst, QCryptoBackend::PinOK); } libcdoc::result_t @@ -281,19 +290,21 @@ 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; } + + TokenData auth = qApp->signer()->tokenauth(); + auto backend = QCryptoBackend::getBackend(auth); + QCryptoBackend::PinStatus pin_status; - auto authKey = dispatchToMain([&] { - return qApp->signer()->key(pin_status); - }); + auto authKey = backend->key(pin_status); + if (!authKey.handle()) { last_error = qApp->signer()->getLastErrorStr().toStdString(); return getDecryptStatus(result, pin_status); diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index 254ebe201..7a74bfde6 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -19,16 +19,180 @@ #include "QCryptoBackend.h" +#include "TokenData.h" +#ifdef Q_OS_WIN +#include "QCNG.h" +#endif +#include "QPKCS11.h" + +#include +#include + +#include +#include +#include + +static QCryptoBackend *static_backend = nullptr; + +static QCryptoBackend * +getBackend() +{ + if(!static_backend) { +#ifdef Q_OS_WIN + backend = new QCNG(); +#else + static_backend = new QPKCS11(); +#endif + } + return static_backend; +} + +std::unique_ptr +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(); + backend->login(token); + return std::move(backend); +} + +QList +QCryptoBackend::getTokens() +{ +#ifdef Q_OS_WIN + return QCNG::tokens(); +#else + return QPKCS11::tokens(); +#endif +} + QString QCryptoBackend::errorString(PinStatus 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 QObject::tr("PIN Canceled"); + case PinLocked: return QObject::tr("PIN locked"); + case PinIncorrect: return QObject::tr("PIN Incorrect"); + case GeneralError: return QObject::tr("PKCS11 general error"); + case DeviceError: return QObject::tr("PKCS11 device error"); + default: return QObject::tr("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::key(QCryptoBackend::PinStatus& pin_status) +{ + QSslKey key = cert.publicKey(); + if(!key.handle()) { + pin_status = QCryptoBackend::GeneralError; + return {}; + } +#if 0 + 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 {}; + } +#endif + 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, this); + } + else + { + RSA *rsa = (RSA*)key.handle(); + RSA_set_method(rsa, get_rsa_method()); + RSA_set_ex_data(rsa, 0, this); + } + return key; +} + +void +QCryptoBackend::shutDown() +{ + delete static_backend; + static_backend = nullptr; + get_rsa_method(true); + get_ec_method(true); +} + diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index 210826b96..7c705ddf6 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -21,12 +21,13 @@ #include #include +#include +#include class TokenData; -class QCryptoBackend: public QObject +class QCryptoBackend { - Q_OBJECT public: enum PinStatus : quint8 { @@ -39,9 +40,8 @@ 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; @@ -51,5 +51,17 @@ class QCryptoBackend: public QObject virtual void logout() = 0; virtual QByteArray sign(QCryptographicHash::Algorithm method, const QByteArray &digest) const = 0; + QSslKey key(PinStatus& pin_status); + + virtual bool reload() { return true; } + + static QCryptoBackend* getBackend(); + static std::unique_ptr getBackend(const TokenData& token); + static void shutDown(); + static QList getTokens(); + static QString errorString( PinStatus error ); + + QSslCertificate cert; + PinStatus status = PinOK; }; diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index 296bd84f2..586c3c36f 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,23 +168,21 @@ 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); + QPKCS11Private *d = getPrivate(); + auto key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); if(key.size() != 1) return {}; @@ -112,15 +196,17 @@ QByteArray QPKCS11::decrypt(const QByteArray &data, bool oaep) const CK_ULONG size = 0; if(d->f->C_Decrypt(d->session, CK_BYTE_PTR(data.constData()), CK_ULONG(data.size()), nullptr, &size) != CKR_OK) 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) 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) return {}; @@ -132,14 +218,14 @@ QByteArray QPKCS11::derive(const QByteArray &publicKey) const CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; CK_ULONG value_len = (publicKey.size() - 1) / 2; - auto newkey_template = std::to_array({ - {CKA_TOKEN, &_false, sizeof(_false)}, - {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, - {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, - {CKA_SENSITIVE, &_false, sizeof(_false)}, - {CKA_EXTRACTABLE, &_true, sizeof(_true)}, - {CKA_VALUE_LEN, &value_len, sizeof(value_len)}, - }); + std::array newkey_template{ + CK_ATTRIBUTE{CKA_TOKEN, &_false, sizeof(_false)}, + CK_ATTRIBUTE{CKA_CLASS, &newkey_class, sizeof(newkey_class)}, + CK_ATTRIBUTE{CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, + CK_ATTRIBUTE{CKA_SENSITIVE, &_false, sizeof(_false)}, + CK_ATTRIBUTE{CKA_EXTRACTABLE, &_true, sizeof(_true)}, + 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) return {}; @@ -147,7 +233,8 @@ QByteArray QPKCS11::derive(const QByteArray &publicKey) const 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 +258,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()) @@ -199,49 +287,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::PinStatus +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 +333,38 @@ 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; + QPKCS11Private *d = getPrivate(); if(!d->f) return list; size_t size = 0; @@ -423,15 +476,16 @@ 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) return {}; @@ -483,12 +537,3 @@ QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &d 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 f9310f3bd..feb3ab3c9 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,10 @@ 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(); + bool reload() final; QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; - QList tokens() const final; -private: - struct Private; - Private *d; + + static QList tokens(); }; diff --git a/client/QPKCS11_p.h b/client/QPKCS11_p.h deleted file mode 100644 index f95870c94..000000000 --- a/client/QPKCS11_p.h +++ /dev/null @@ -1,41 +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 - -struct QPKCS11::Private -{ - 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..dbc034266 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -57,51 +57,15 @@ class QSigner::Private final 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); + //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()); + //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; @@ -110,16 +74,6 @@ 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 +110,6 @@ QSigner::~QSigner() d->sleepCond.wakeAll(); wait(); delete d->smartcard; - RSA_meth_free(d->rsamethod); - EC_KEY_METHOD_free(d->ecmethod); delete d; } @@ -171,84 +123,6 @@ 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)) @@ -306,22 +180,20 @@ void QSigner::run() #ifdef Q_OS_WIN d->backend = new QCNG(this); #else - d->backend = new QPKCS11(this); + d->backend = new QPKCS11(); #endif while(!isInterruptionRequested()) { if(d->lock.tryLockForRead()) { - auto *pkcs11 = qobject_cast(d->backend); - if(pkcs11 && !pkcs11->reload()) - { + if (!d->backend->reload()) { Q_EMIT error(tr("Failed to load PKCS#11 module"), {}); return; } QList acards, scards; - QList cache = d->backend->tokens(); + QList cache = QCryptoBackend::getTokens(); if(cache != d->cache) { d->cache = std::move(cache); @@ -374,6 +246,7 @@ void QSigner::run() break; d->sleepCond.wait(&d->sleepMutex, 5000); } + QCryptoBackend::shutDown(); } void QSigner::selectCard(const TokenData &token) diff --git a/client/QSigner.h b/client/QSigner.h index 286320631..02d9e0e55 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, From 265f3eccb99503eed5f45f5d3a4a421e349b1ab3 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 17 Apr 2026 15:46:21 +0300 Subject: [PATCH 02/15] More cleanups --- client/CDocSupport.cpp | 11 ++---- client/QCryptoBackend.cpp | 20 ++-------- client/QCryptoBackend.h | 6 +-- client/QPKCS11.cpp | 1 + client/QPKCS11.h | 2 +- client/QSigner.cpp | 77 +++++++++++++-------------------------- client/QSigner.h | 1 - 7 files changed, 36 insertions(+), 82 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 9a94a0ffe..88c6fcc90 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -302,12 +302,10 @@ DDNetworkBackend::fetchKey(std::vector &result, const std::string &url, TokenData auth = qApp->signer()->tokenauth(); auto backend = QCryptoBackend::getBackend(auth); - QCryptoBackend::PinStatus pin_status; - auto authKey = backend->key(pin_status); - + 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)); @@ -315,9 +313,6 @@ DDNetworkBackend::fetchKey(std::vector &result, const std::string &url, 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(); diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index 7a74bfde6..e240c2376 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -55,7 +55,9 @@ QCryptoBackend::getBackend(const TokenData& token) { auto backend = std::make_unique(); #endif backend.get()->cert = token.cert(); - backend->login(token); + do { + backend.get()->status = backend->login(token); + } while (backend.get()->status == PinIncorrect); return std::move(backend); } @@ -152,26 +154,12 @@ static EC_KEY_METHOD *get_ec_method(bool release = false) } QSslKey -QCryptoBackend::key(QCryptoBackend::PinStatus& pin_status) +QCryptoBackend::getKey() { QSslKey key = cert.publicKey(); if(!key.handle()) { - pin_status = QCryptoBackend::GeneralError; return {}; } -#if 0 - 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 {}; - } -#endif if(key.algorithm() == QSsl::Ec) { auto *ec = (EC_KEY*)key.handle(); diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index 7c705ddf6..c49b58dc1 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -46,15 +46,13 @@ class QCryptoBackend 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; + QSslKey getKey(); + 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; - QSslKey key(PinStatus& pin_status); - - virtual bool reload() { return true; } - static QCryptoBackend* getBackend(); static std::unique_ptr getBackend(const TokenData& token); static void shutDown(); diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index 586c3c36f..682532ca8 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -364,6 +364,7 @@ void QPKCS11::logout() QList QPKCS11::tokens() { QList list; + reload(); QPKCS11Private *d = getPrivate(); if(!d->f) return list; diff --git a/client/QPKCS11.h b/client/QPKCS11.h index feb3ab3c9..99684c275 100644 --- a/client/QPKCS11.h +++ b/client/QPKCS11.h @@ -34,7 +34,7 @@ class QPKCS11 final: public QCryptoBackend QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const final; PinStatus login(const TokenData &t) final; void logout() final; - bool reload() final; + static bool reload(); QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; static QList tokens(); diff --git a/client/QSigner.cpp b/client/QSigner.cpp index dbc034266..0e2cad522 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -49,7 +49,6 @@ static Q_LOGGING_CATEGORY(SLog, "qdigidoc4.QSigner") class QSigner::Private final { public: - QCryptoBackend *backend {}; QSmartCard *smartcard {}; TokenData auth, sign; QList cache; @@ -123,30 +122,8 @@ X509Cert QSigner::cert() const return X509Cert((const unsigned char*)der.constData(), size_t(der.size()), X509Cert::Der); } -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); @@ -177,21 +154,9 @@ void QSigner::run() { d->auth.clear(); d->sign.clear(); -#ifdef Q_OS_WIN - d->backend = new QCNG(this); -#else - d->backend = new QPKCS11(); -#endif - - while(!isInterruptionRequested()) - { - if(d->lock.tryLockForRead()) - { - if (!d->backend->reload()) { - Q_EMIT error(tr("Failed to load PKCS#11 module"), {}); - return; - } + while(!isInterruptionRequested()) { + if(d->lock.tryLockForRead()) { QList acards, scards; QList cache = QCryptoBackend::getTokens(); if(cache != d->cache) @@ -288,23 +253,32 @@ 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 backend = QCryptoBackend::getBackend(d->sign); + while (backend->status == QCryptoBackend::PinIncorrect) { + dispatchToMain([&]{ + WarningDialog::create() + ->withTitle(SslCertificate(d->sign.cert()).keyUsage().contains(SslCertificate::NonRepudiation) ? tr("Failed to sign document") : tr("Failed to decrypt document")) + ->withText(QCryptoBackend::errorString(backend->status)) + ->exec(); + }); + backend->login(d->sign); + } + if (backend->status != QCryptoBackend::PinOK) { + switch(backend->status) { + case QCryptoBackend::PinCanceled: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINCanceled); + case QCryptoBackend::PinLocked: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINLocked); + default: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINFailed); + } } - QByteArray sig = waitFor(&QCryptoBackend::sign, d->backend, + QByteArray sig = waitFor(&QCryptoBackend::sign, backend.get(), methodToNID(method), QByteArray::fromRawData((const char*)digest.data(), int(digest.size()))); - logout(); - if(d->backend->lastError() == QCryptoBackend::PinCanceled) + if (backend->lastError() == QCryptoBackend::PinCanceled) throwException(tr("Failed to login token"), Exception::PINCanceled) - if( sig.isEmpty() ) + if (sig.isEmpty()) throwException(tr("Failed to sign document"), Exception::General) return {sig.constBegin(), sig.constEnd()}; } @@ -316,6 +290,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 02d9e0e55..92cc91a6a 100644 --- a/client/QSigner.h +++ b/client/QSigner.h @@ -57,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; From 6fb2d6a39cacd538f03c243da95947679f904a36 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Mon, 20 Apr 2026 18:44:56 +0300 Subject: [PATCH 03/15] More cleanup, use std::expected --- client/CDocSupport.cpp | 39 ++++++++++++++++++++------------------ client/QCNG.cpp | 6 +++--- client/QCNG.h | 4 ++-- client/QCryptoBackend.cpp | 32 ++++++++++--------------------- client/QCryptoBackend.h | 40 +++++++++++++++++++++++++++++---------- client/QPKCS11.cpp | 2 +- client/QPKCS11.h | 2 +- client/QSigner.cpp | 26 ++++++++----------------- 8 files changed, 76 insertions(+), 75 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 88c6fcc90..ce6c4a3d0 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: @@ -112,15 +112,15 @@ getDecryptStatus(const std::vector& result, QCryptoBackend::PinStatus p libcdoc::result_t DDCryptoBackend::decryptRSA(std::vector& dst, const std::vector &data, bool oaep, unsigned int idx) { - auto backend = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); - if (backend->status != QCryptoBackend::PinOK) { - return getDecryptStatus({}, backend->status); - } + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + std::unique_ptr backend(val.value()); // fixme: Locking // fixme: missing token QByteArray decryptedKey = backend->decrypt(toByteArray(data), oaep); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); - return getDecryptStatus(dst, QCryptoBackend::PinOK); + return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); } libcdoc::result_t @@ -132,30 +132,30 @@ DDCryptoBackend::deriveConcatKDF(std::vector& dst, const std::vectorsigner()->tokenauth()); - if (backend->status != QCryptoBackend::PinOK) { - return getDecryptStatus({}, backend->status); - } + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + std::unique_ptr backend(val.value()); // fixme: Locking // fixme: missing token 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, QCryptoBackend::PinOK); + 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) { - auto backend = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); - if (backend->status != QCryptoBackend::PinOK) { - return getDecryptStatus({}, backend->status); - } + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + std::unique_ptr backend(val.value()); // fixme: Locking // fixme: missing token QByteArray decryptedKey = backend->deriveHMACExtract(toByteArray(key_material), toByteArray(salt), ECC_KEY_LEN); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); - return getDecryptStatus(dst, QCryptoBackend::PinOK); + return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); } libcdoc::result_t @@ -300,7 +300,10 @@ DDNetworkBackend::fetchKey(std::vector &result, const std::string &url, } TokenData auth = qApp->signer()->tokenauth(); - auto backend = QCryptoBackend::getBackend(auth); + 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()) { diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 7107299e5..8201948e2 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -45,7 +45,7 @@ class QCNG::Private { public: TokenData token; - QCNG::PinStatus err = QCNG::PinOK; + QCNG::Status err = QCNG::PinOK; }; QCNG::QCNG( QObject *parent ) @@ -179,7 +179,7 @@ QByteArray QCNG::exec(F &&func) const } } -QCNG::PinStatus QCNG::lastError() const { return d->err; } +QCNG::Status QCNG::lastError() const { return d->err; } QList QCNG::tokens() const { @@ -275,7 +275,7 @@ QList QCNG::tokens() const return result; } -QCNG::PinStatus QCNG::login(const TokenData &token) +QCNG::Status QCNG::login(const TokenData &token) { d->token = token; return d->err = QCNG::PinOK; diff --git a/client/QCNG.h b/client/QCNG.h index 279860ab5..80dce0356 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -36,8 +36,8 @@ class QCNG 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; - PinStatus lastError() const final; - PinStatus login(const TokenData &token) final; + Status lastError() const final; + Status login(const TokenData &token) final; void logout() final {} QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index e240c2376..3da5f4870 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -28,26 +28,14 @@ #include #include +// TODO: Port everything to the new OpenSSL API +#define OPENSSL_SUPPRESS_DEPRECATED + #include #include #include -static QCryptoBackend *static_backend = nullptr; - -static QCryptoBackend * -getBackend() -{ - if(!static_backend) { -#ifdef Q_OS_WIN - backend = new QCNG(); -#else - static_backend = new QPKCS11(); -#endif - } - return static_backend; -} - -std::unique_ptr +std::expected QCryptoBackend::getBackend(const TokenData& token) { #ifdef Q_OS_WIN auto backend = std::make_unique(); @@ -55,10 +43,12 @@ QCryptoBackend::getBackend(const TokenData& token) { auto backend = std::make_unique(); #endif backend.get()->cert = token.cert(); + Status status; do { - backend.get()->status = backend->login(token); - } while (backend.get()->status == PinIncorrect); - return std::move(backend); + status = backend->login(token); + } while (status == PinIncorrect); + if (status != PinOK) return std::unexpected(status); + return backend.release(); } QList @@ -71,7 +61,7 @@ QCryptoBackend::getTokens() #endif } -QString QCryptoBackend::errorString(PinStatus error) +QString QCryptoBackend::errorString(Status error) { switch( error ) { @@ -178,8 +168,6 @@ QCryptoBackend::getKey() void QCryptoBackend::shutDown() { - delete static_backend; - static_backend = nullptr; get_rsa_method(true); get_ec_method(true); } diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index c49b58dc1..43e0a9b46 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -24,12 +24,14 @@ #include #include +#include + class TokenData; class QCryptoBackend { public: - enum PinStatus : quint8 + enum Status : quint8 { PinOK, PinCanceled, @@ -46,20 +48,38 @@ class QCryptoBackend 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; - QSslKey getKey(); - - 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 QCryptoBackend* getBackend(); - static std::unique_ptr getBackend(const TokenData& token); + /** + * @brief Get the SSL key for the certificate + * + * @return the Qt SSL key + */ + QSslKey getKey(); + /** + * @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 Get a list of all available tokens + * + * @return list of all available tokens + */ static QList getTokens(); - static QString errorString( PinStatus error ); + static QString errorString(Status error); +protected: + virtual Status login(const TokenData &cert) = 0; + virtual void logout() = 0; QSslCertificate cert; - PinStatus status = PinOK; }; diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index 682532ca8..469e60ac6 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -287,7 +287,7 @@ QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, return out; } -QPKCS11::PinStatus +QPKCS11::Status QPKCS11::login(const TokenData &t) { logout(); diff --git a/client/QPKCS11.h b/client/QPKCS11.h index 99684c275..6359ed8f8 100644 --- a/client/QPKCS11.h +++ b/client/QPKCS11.h @@ -32,7 +32,7 @@ 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; - PinStatus login(const TokenData &t) final; + Status login(const TokenData &t) final; void logout() final; static bool reload(); QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; diff --git a/client/QSigner.cpp b/client/QSigner.cpp index 0e2cad522..6a8fccd3d 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -253,31 +253,21 @@ std::vector QSigner::sign(const std::string &method, const std::v throwException(tr("Signing certificate is not selected."), Exception::General) } - auto backend = QCryptoBackend::getBackend(d->sign); - while (backend->status == QCryptoBackend::PinIncorrect) { - dispatchToMain([&]{ - WarningDialog::create() - ->withTitle(SslCertificate(d->sign.cert()).keyUsage().contains(SslCertificate::NonRepudiation) ? tr("Failed to sign document") : tr("Failed to decrypt document")) - ->withText(QCryptoBackend::errorString(backend->status)) - ->exec(); - }); - backend->login(d->sign); - } - if (backend->status != QCryptoBackend::PinOK) { - switch(backend->status) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) { + switch(val.error()) { case QCryptoBackend::PinCanceled: - throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINCanceled); + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINCanceled); case QCryptoBackend::PinLocked: - throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINLocked); + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINLocked); default: - throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINFailed); + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINFailed); } } + std::unique_ptr backend(val.value()); + QByteArray sig = waitFor(&QCryptoBackend::sign, backend.get(), methodToNID(method), QByteArray::fromRawData((const char*)digest.data(), int(digest.size()))); - if (backend->lastError() == QCryptoBackend::PinCanceled) - throwException(tr("Failed to login token"), Exception::PINCanceled) - if (sig.isEmpty()) throwException(tr("Failed to sign document"), Exception::General) return {sig.constBegin(), sig.constEnd()}; From 920df7b26d73f1924376bd3282010d21e633caf7 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Wed, 22 Apr 2026 11:30:15 +0300 Subject: [PATCH 04/15] QCNG cleanup --- client/QCNG.cpp | 56 +++++++++++++++++------------------------ client/QCNG.h | 24 ++++++++---------- client/QCryptoBackend.h | 14 ++++++++--- client/QPKCS11.cpp | 10 ++++---- client/QPKCS11.h | 11 ++++---- 5 files changed, 55 insertions(+), 60 deletions(-) diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 8201948e2..0e0d4c8ec 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -41,25 +41,23 @@ struct SCOPE constexpr T* operator&() noexcept { return &d; } }; -class QCNG::Private +QCNG::QCNG() { -public: - TokenData token; - QCNG::Status err = QCNG::PinOK; -}; - -QCNG::QCNG( QObject *parent ) - : QCryptoBackend(parent) - , d(new Private) -{} +} QCNG::~QCNG() { - delete d; } -QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const +QCNG::Status QCNG::login(const TokenData &_token) +{ + token = std::make_unique(_token); + return PinOK; +} + +QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) { + Status status = PinOK; 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; @@ -79,7 +77,7 @@ QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const } template -QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) const +QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) { 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) }; @@ -103,7 +101,7 @@ 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 + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) { return derive(publicKey, [&](NCRYPT_SECRET_HANDLE sharedSecret, QByteArray &derived) { std::array paramValues{ @@ -133,7 +131,7 @@ QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash }); } -QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const +QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) { return derive(publicKey, [&](NCRYPT_SECRET_HANDLE sharedSecret, QByteArray &derived) { std::array paramValues{ @@ -153,15 +151,15 @@ QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray } template -QByteArray QCNG::exec(F &&func) const +QByteArray QCNG::exec(F &&func) { - d->err = UnknownError; + status = UnknownError; SCOPE prov; - if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(d->token.data(u"provider"_s).toString().utf16()), 0))) + if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(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))) + if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(token->data(u"key"_s).toString().utf16()), + 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); @@ -169,19 +167,17 @@ QByteArray QCNG::exec(F &&func) const switch(func(prov, 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::Status QCNG::lastError() const { return d->err; } - -QList QCNG::tokens() const +QList QCNG::tokens() { QList result; auto prop = [](NCRYPT_HANDLE handle, LPCWSTR param) -> QByteArray { @@ -275,13 +271,7 @@ QList QCNG::tokens() const return result; } -QCNG::Status QCNG::login(const TokenData &token) -{ - d->token = token; - return d->err = QCNG::PinOK; -} - -QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const +QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) { return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) { BCRYPT_PSS_PADDING_INFO rsaPSS { NCRYPT_SHA256_ALGORITHM, 32 }; @@ -301,7 +291,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 && token->data(u"PSS"_s).toBool()) { padding = BCRYPT_PAD_PSS; paddingInfo = &rsaPSS; diff --git a/client/QCNG.h b/client/QCNG.h index 80dce0356..b583c3dd6 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -26,27 +26,25 @@ class QCNG final: public QCryptoBackend { - Q_OBJECT public: - explicit QCNG(QObject *parent = nullptr); + explicit QCNG(); ~QCNG() final; - QList tokens() const 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; - Status lastError() const final; Status login(const TokenData &token) final; void logout() final {} - QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; + QByteArray decrypt(const QByteArray &data, bool oaep) final; + QByteArray deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) final; + QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) final; + QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) final; + + static QList tokens(); private: template - QByteArray derive(const QByteArray &publicKey, F &&func) const; + QByteArray derive(const QByteArray &publicKey, F &&func); template - QByteArray exec(F &&func) const; + QByteArray exec(F &&func); - class Private; - Private *d; + std::unique_ptr token; }; diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index 43e0a9b46..8f1d3cde1 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -44,11 +44,11 @@ class QCryptoBackend virtual ~QCryptoBackend() {}; - virtual QByteArray decrypt(const QByteArray &data, bool oaep) const = 0; + virtual QByteArray decrypt(const QByteArray &data, bool oaep) = 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 QByteArray sign(QCryptographicHash::Algorithm method, const QByteArray &digest) const = 0; + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) = 0; + virtual QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) = 0; + virtual QByteArray sign(QCryptographicHash::Algorithm method, const QByteArray &digest) = 0; /** * @brief Get the SSL key for the certificate @@ -69,6 +69,12 @@ class QCryptoBackend * 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 + */ + Status status = PinOK; + /** * @brief Get a list of all available tokens * diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index 469e60ac6..b242ac4d4 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -179,7 +179,7 @@ QPKCS11::~QPKCS11() } QByteArray -QPKCS11::decrypt(const QByteArray &data, bool oaep) const +QPKCS11::decrypt(const QByteArray &data, bool oaep) { QPKCS11Private *d = getPrivate(); auto key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); @@ -204,7 +204,7 @@ QPKCS11::decrypt(const QByteArray &data, bool oaep) const } QByteArray -QPKCS11::derive(const QByteArray &publicKey) const +QPKCS11::derive(const QByteArray &publicKey) { QPKCS11Private *d = getPrivate(); std::vector key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); @@ -235,7 +235,7 @@ QPKCS11::derive(const QByteArray &publicKey) const QByteArray QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, - const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) { QByteArray z = derive(publicKey); if(z.isEmpty()) @@ -259,7 +259,7 @@ QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algori } QByteArray -QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const +QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) { QByteArray key = derive(publicKey); if(key.isEmpty()) @@ -484,7 +484,7 @@ bool QPKCS11::reload() return QPKCS11Private::load(drivers.key({})); } -QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const +QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) { QPKCS11Private *d = getPrivate(); std::vector key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); diff --git a/client/QPKCS11.h b/client/QPKCS11.h index 6359ed8f8..668073b7c 100644 --- a/client/QPKCS11.h +++ b/client/QPKCS11.h @@ -27,15 +27,16 @@ class QPKCS11 final: public QCryptoBackend explicit QPKCS11(); ~QPKCS11() final; - QByteArray decrypt(const QByteArray &data, bool oaep) const final; - QByteArray derive(const QByteArray &publicKey) const; + QByteArray decrypt(const QByteArray &data, bool oaep) final; + QByteArray derive(const QByteArray &publicKey); 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; + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) final; + QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) final; + QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) final; + Status login(const TokenData &t) final; void logout() final; static bool reload(); - QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; static QList tokens(); }; From 8c4a090554da1472ccbe78ba1c22d416f76cc069 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 22 Apr 2026 13:32:09 +0300 Subject: [PATCH 05/15] Share backend between network/crypto --- client/CDocSupport.cpp | 36 +++++++++++++++++++++----------- client/CDocSupport.h | 10 ++++++++- client/CryptoDoc.cpp | 1 + client/QCryptoBackend.cpp | 1 + client/QPKCS11.cpp | 43 +++++++++++++++++++++++++++++---------- client/QSigner.cpp | 2 +- 6 files changed, 68 insertions(+), 25 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index ce6c4a3d0..a118a5443 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -112,14 +112,17 @@ getDecryptStatus(QCryptoBackend::Status pin_status) libcdoc::result_t DDCryptoBackend::decryptRSA(std::vector& dst, const std::vector &data, bool oaep, unsigned int idx) { - auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); - if (!val.value()) - return getDecryptStatus(val.error()); - std::unique_ptr backend(val.value()); + if (!backend) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + backend.reset(val.value()); + } // fixme: Locking // fixme: missing token QByteArray decryptedKey = backend->decrypt(toByteArray(data), oaep); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); + backend.reset(); return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); } @@ -132,29 +135,35 @@ DDCryptoBackend::deriveConcatKDF(std::vector& dst, const std::vectorsigner()->tokenauth()); - if (!val.value()) - return getDecryptStatus(val.error()); - std::unique_ptr backend(val.value()); + if (!backend) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + backend.reset(val.value()); + } // fixme: Locking // fixme: missing token QByteArray decryptedKey = backend->deriveConcatKDF(toByteArray(publicKey), SHA_MTH.value(digest), toByteArray(algorithmID), toByteArray(partyUInfo), toByteArray(partyVInfo)); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); + 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) { - auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); - if (!val.value()) - return getDecryptStatus(val.error()); - std::unique_ptr backend(val.value()); + if (!backend) { + auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + if (!val.value()) + return getDecryptStatus(val.error()); + backend.reset(val.value()); + } // fixme: Locking // fixme: missing token 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); } @@ -324,6 +333,9 @@ DDNetworkBackend::fetchKey(std::vector &result, const std::string &url, 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(backend.release()); + return libcdoc::OK; } diff --git a/client/CDocSupport.h b/client/CDocSupport.h index b3c87bd90..014cf2eae 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(QCryptoBackend *backend) { + this->backend.reset(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 81af61290..ffb212969 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 { diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index 3da5f4870..a38f03720 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -148,6 +148,7 @@ QCryptoBackend::getKey() { QSslKey key = cert.publicKey(); if(!key.handle()) { + status = GeneralError; return {}; } if(key.algorithm() == QSsl::Ec) diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index b242ac4d4..6121a0ae3 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -183,22 +183,30 @@ QPKCS11::decrypt(const QByteArray &data, bool oaep) { QPKCS11Private *d = getPrivate(); auto key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); - if(key.size() != 1) + 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; } @@ -208,8 +216,10 @@ QPKCS11::derive(const QByteArray &publicKey) { 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) }; @@ -227,8 +237,10 @@ QPKCS11::derive(const QByteArray &publicKey) 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); } @@ -267,12 +279,13 @@ QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, 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; }; @@ -488,8 +501,10 @@ QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &d { 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) }; @@ -528,13 +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; } diff --git a/client/QSigner.cpp b/client/QSigner.cpp index 6a8fccd3d..af4c1e03d 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -253,7 +253,7 @@ std::vector QSigner::sign(const std::string &method, const std::v throwException(tr("Signing certificate is not selected."), Exception::General) } - auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); + auto val = QCryptoBackend::getBackend(d->sign); if (!val.value()) { switch(val.error()) { case QCryptoBackend::PinCanceled: From d0c44e96f507112ee81dd59354b20ac053be2020 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 22 Apr 2026 15:24:56 +0300 Subject: [PATCH 06/15] Use const/mutable --- client/CDocSupport.cpp | 6 ------ client/QCNG.cpp | 22 +++++++++++----------- client/QCNG.h | 14 +++++++------- client/QCryptoBackend.cpp | 19 ++++++++++--------- client/QCryptoBackend.h | 14 ++++++-------- client/QPKCS11.cpp | 10 +++++----- client/QPKCS11.h | 10 +++++----- client/QSigner.cpp | 10 ---------- 8 files changed, 44 insertions(+), 61 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index a118a5443..b7906dcaf 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -118,8 +118,6 @@ DDCryptoBackend::decryptRSA(std::vector& dst, const std::vectordecrypt(toByteArray(data), oaep); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); backend.reset(); @@ -141,8 +139,6 @@ DDCryptoBackend::deriveConcatKDF(std::vector& dst, const std::vectorderiveConcatKDF(toByteArray(publicKey), SHA_MTH.value(digest), toByteArray(algorithmID), toByteArray(partyUInfo), toByteArray(partyVInfo)); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); @@ -159,8 +155,6 @@ DDCryptoBackend::deriveHMACExtract(std::vector& dst, const std::vector< return getDecryptStatus(val.error()); backend.reset(val.value()); } - // fixme: Locking - // fixme: missing token QByteArray decryptedKey = backend->deriveHMACExtract(toByteArray(key_material), toByteArray(salt), ECC_KEY_LEN); dst.assign(decryptedKey.cbegin(), decryptedKey.cend()); backend.reset(); diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 0e0d4c8ec..b8ff7b1e2 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -51,11 +51,11 @@ QCNG::~QCNG() QCNG::Status QCNG::login(const TokenData &_token) { - token = std::make_unique(_token); + token = _token; return PinOK; } -QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) +QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const { Status status = PinOK; return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) { @@ -77,7 +77,7 @@ QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) } template -QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) +QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) const { 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) }; @@ -101,7 +101,7 @@ QByteArray QCNG::derive(const QByteArray &publicKey, F &&func) } QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, - const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const { return derive(publicKey, [&](NCRYPT_SECRET_HANDLE sharedSecret, QByteArray &derived) { std::array paramValues{ @@ -131,7 +131,7 @@ QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash }); } -QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) +QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const { return derive(publicKey, [&](NCRYPT_SECRET_HANDLE sharedSecret, QByteArray &derived) { std::array paramValues{ @@ -151,15 +151,15 @@ QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray } template -QByteArray QCNG::exec(F &&func) +QByteArray QCNG::exec(F &&func) const { status = UnknownError; SCOPE prov; - if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(token->data(u"provider"_s).toString().utf16()), 0))) + if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(token.data(u"provider"_s).toString().utf16()), 0))) return {}; SCOPE key; - if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(token->data(u"key"_s).toString().utf16()), - token->data(u"spec"_s).value(), 0))) + if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(token.data(u"key"_s).toString().utf16()), + 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); @@ -271,7 +271,7 @@ QList QCNG::tokens() return result; } -QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) +QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const { return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) { BCRYPT_PSS_PADDING_INFO rsaPSS { NCRYPT_SHA256_ALGORITHM, 32 }; @@ -291,7 +291,7 @@ QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &dige bool isRSA = algo == QLatin1String("RSA"); DWORD padding {}; PVOID paddingInfo {}; - if(isRSA && token->data(u"PSS"_s).toBool()) + if(isRSA && token.data(u"PSS"_s).toBool()) { padding = BCRYPT_PAD_PSS; paddingInfo = &rsaPSS; diff --git a/client/QCNG.h b/client/QCNG.h index b583c3dd6..80de2a0f0 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -33,18 +33,18 @@ class QCNG final: public QCryptoBackend Status login(const TokenData &token) final; void logout() final {} - QByteArray decrypt(const QByteArray &data, bool oaep) 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) final; - QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) final; - QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) final; + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const final; + QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const final; + QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; static QList tokens(); private: template - QByteArray derive(const QByteArray &publicKey, F &&func); + QByteArray derive(const QByteArray &publicKey, F &&func) const; template - QByteArray exec(F &&func); + QByteArray exec(F &&func) const; - std::unique_ptr token; + TokenData token; }; diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index a38f03720..c376481f7 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -25,6 +25,7 @@ #endif #include "QPKCS11.h" +#include #include #include @@ -66,12 +67,12 @@ QString QCryptoBackend::errorString(Status error) switch( error ) { case PinOK: return QString(); - case PinCanceled: return QObject::tr("PIN Canceled"); - case PinLocked: return QObject::tr("PIN locked"); - case PinIncorrect: return QObject::tr("PIN Incorrect"); - case GeneralError: return QObject::tr("PKCS11 general error"); - case DeviceError: return QObject::tr("PKCS11 device error"); - default: return QObject::tr("Unknown error"); + case PinCanceled: return QCoreApplication::translate("QCryptoBackend", "PIN 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"); } } @@ -144,7 +145,7 @@ static EC_KEY_METHOD *get_ec_method(bool release = false) } QSslKey -QCryptoBackend::getKey() +QCryptoBackend::getKey() const { QSslKey key = cert.publicKey(); if(!key.handle()) { @@ -155,13 +156,13 @@ QCryptoBackend::getKey() { auto *ec = (EC_KEY*)key.handle(); EC_KEY_set_method(ec, get_ec_method()); - EC_KEY_set_ex_data(ec, 0, this); + 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, this); + RSA_set_ex_data(rsa, 0, (void *) this); } return key; } diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index 8f1d3cde1..a9cc5e13c 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -19,8 +19,6 @@ #pragma once -#include -#include #include #include @@ -44,18 +42,18 @@ class QCryptoBackend virtual ~QCryptoBackend() {}; - virtual QByteArray decrypt(const QByteArray &data, bool oaep) = 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) = 0; - virtual QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) = 0; - virtual QByteArray sign(QCryptographicHash::Algorithm method, const QByteArray &digest) = 0; + 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 QByteArray sign(QCryptographicHash::Algorithm method, const QByteArray &digest) const = 0; /** * @brief Get the SSL key for the certificate * * @return the Qt SSL key */ - QSslKey getKey(); + QSslKey getKey() const; /** * @brief Get a new Backend object and log in with the given token * @@ -73,7 +71,7 @@ class QCryptoBackend /** * @brief The status of the last operation */ - Status status = PinOK; + mutable Status status = PinOK; /** * @brief Get a list of all available tokens diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index 6121a0ae3..4c9b785c2 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -179,7 +179,7 @@ QPKCS11::~QPKCS11() } QByteArray -QPKCS11::decrypt(const QByteArray &data, bool oaep) +QPKCS11::decrypt(const QByteArray &data, bool oaep) const { QPKCS11Private *d = getPrivate(); auto key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); @@ -212,7 +212,7 @@ QPKCS11::decrypt(const QByteArray &data, bool oaep) } QByteArray -QPKCS11::derive(const QByteArray &publicKey) +QPKCS11::derive(const QByteArray &publicKey) const { QPKCS11Private *d = getPrivate(); std::vector key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); @@ -247,7 +247,7 @@ QPKCS11::derive(const QByteArray &publicKey) QByteArray QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, - const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const { QByteArray z = derive(publicKey); if(z.isEmpty()) @@ -271,7 +271,7 @@ QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algori } QByteArray -QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) +QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const { QByteArray key = derive(publicKey); if(key.isEmpty()) @@ -497,7 +497,7 @@ bool QPKCS11::reload() return QPKCS11Private::load(drivers.key({})); } -QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) +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); diff --git a/client/QPKCS11.h b/client/QPKCS11.h index 668073b7c..fbc80e10b 100644 --- a/client/QPKCS11.h +++ b/client/QPKCS11.h @@ -27,12 +27,12 @@ class QPKCS11 final: public QCryptoBackend explicit QPKCS11(); ~QPKCS11() final; - QByteArray decrypt(const QByteArray &data, bool oaep) final; - QByteArray derive(const QByteArray &publicKey); + QByteArray decrypt(const QByteArray &data, bool oaep) const final; + QByteArray derive(const QByteArray &publicKey) const; QByteArray deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, - const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) final; - QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) final; - QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) final; + const QByteArray &algorithmID, const QByteArray &partyUInfo, const QByteArray &partyVInfo) const final; + QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const final; + QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; Status login(const TokenData &t) final; void logout() final; diff --git a/client/QSigner.cpp b/client/QSigner.cpp index af4c1e03d..790ce23e4 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -55,18 +55,8 @@ class QSigner::Private final 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()); }; - - using namespace digidoc; QSigner::QSigner(QObject *parent) From d64317a664ff2950903728948339f8ce274843fc Mon Sep 17 00:00:00 2001 From: lauris71 Date: Wed, 22 Apr 2026 14:03:58 +0300 Subject: [PATCH 07/15] Remove extra status --- client/QCNG.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/client/QCNG.cpp b/client/QCNG.cpp index b8ff7b1e2..fc6cbb11f 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -57,7 +57,6 @@ QCNG::Status QCNG::login(const TokenData &_token) QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const { - Status status = PinOK; 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; From ec1b0194dd821c5a08ee2f547db372ab092bbdfa Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 22 Apr 2026 15:43:42 +0300 Subject: [PATCH 08/15] Include TokenData.h --- client/QCNG.h | 1 + 1 file changed, 1 insertion(+) diff --git a/client/QCNG.h b/client/QCNG.h index 80de2a0f0..98b297991 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -20,6 +20,7 @@ #pragma once #include "QCryptoBackend.h" +#include "TokenData.h" #include #include From b7cea1cc747f8959092708c3b1b962a17936b592 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 24 Apr 2026 09:24:20 +0300 Subject: [PATCH 09/15] Pass Backend as unique ptr --- client/CDocSupport.cpp | 2 +- client/CDocSupport.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index b7906dcaf..5fc485072 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -328,7 +328,7 @@ DDNetworkBackend::fetchKey(std::vector &result, const std::string &url, QByteArray key_material = QByteArray::fromBase64(json.value(QLatin1String("ephemeral_key_material")).toString().toLatin1()); result.assign(key_material.cbegin(), key_material.cend()); - crypto.setBackend(backend.release()); + crypto.setBackend(std::move(backend)); return libcdoc::OK; } diff --git a/client/CDocSupport.h b/client/CDocSupport.h index 014cf2eae..42f6efc12 100644 --- a/client/CDocSupport.h +++ b/client/CDocSupport.h @@ -83,8 +83,8 @@ struct DDCryptoBackend final : public libcdoc::CryptoBackend { explicit DDCryptoBackend() = default; - void setBackend(QCryptoBackend *backend) { - this->backend.reset(backend); + void setBackend(std::unique_ptr &&backend) { + this->backend = std::move(backend); } }; From 3871c4ce2358b968568fc028ee952deca4fcefe3 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 27 Apr 2026 16:16:34 +0300 Subject: [PATCH 10/15] Open QCNG keys on login and keep until logout/delete --- client/Application.cpp | 4 ++++ client/QCNG.cpp | 49 +++++++++++++++++++++++++++++++----------- client/QCNG.h | 5 +++-- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/client/Application.cpp b/client/Application.cpp index e164686e8..7a20bc2cf 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 ) diff --git a/client/QCNG.cpp b/client/QCNG.cpp index fc6cbb11f..90ee32437 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -41,6 +41,13 @@ struct SCOPE constexpr T* operator&() noexcept { return &d; } }; +struct QCNG::Private +{ + SCOPE prov; + SCOPE key; + bool pss; +}; + QCNG::QCNG() { } @@ -49,14 +56,31 @@ QCNG::~QCNG() { } -QCNG::Status QCNG::login(const TokenData &_token) +QCNG::Status QCNG::login(const TokenData &token) { - token = _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; @@ -78,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) @@ -102,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())}, @@ -132,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())}, @@ -153,17 +183,8 @@ template QByteArray QCNG::exec(F &&func) const { status = UnknownError; - SCOPE prov; - if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(token.data(u"provider"_s).toString().utf16()), 0))) - return {}; - SCOPE key; - if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(token.data(u"key"_s).toString().utf16()), - 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); QByteArray result; - switch(func(prov, key, result)) + switch(func(d->prov, d->key, result)) { case ERROR_SUCCESS: status = PinOK; @@ -272,6 +293,8 @@ QList QCNG::tokens() 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) @@ -290,7 +313,7 @@ QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &dige bool isRSA = algo == QLatin1String("RSA"); DWORD padding {}; PVOID paddingInfo {}; - if(isRSA && 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 98b297991..c79e5f155 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -32,7 +32,7 @@ class QCNG final: public QCryptoBackend ~QCNG() final; Status login(const TokenData &token) final; - void logout() final {} + void logout() final; QByteArray decrypt(const QByteArray &data, bool oaep) const final; QByteArray deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algorithm digest, @@ -47,5 +47,6 @@ class QCNG final: public QCryptoBackend template QByteArray exec(F &&func) const; - TokenData token; + struct Private; + std::unique_ptr d; }; From 060dca457d9bd6e785095bb4966a15c2a18e3b62 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Mon, 27 Apr 2026 16:25:07 +0300 Subject: [PATCH 11/15] Removed notification about entering PIN twice for CDoc2 online container --- client/CryptoDoc.cpp | 22 ---------------------- client/Settings.cpp | 1 - client/Settings.h | 1 - 3 files changed, 24 deletions(-) diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index ffb212969..893f8f038 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -341,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; diff --git a/client/Settings.cpp b/client/Settings.cpp index f0ed78eef..0a3e3e767 100644 --- a/client/Settings.cpp +++ b/client/Settings.cpp @@ -30,7 +30,6 @@ using Option = Settings::Option; const Option Settings::CDOC2_DEFAULT { QStringLiteral("CDOC2-DEFAULT"), [] { return Application::confValue(QLatin1String("CDOC2-DEFAULT")).toBool(false); }}; -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..b2355c41f 100644 --- a/client/Settings.h +++ b/client/Settings.h @@ -75,7 +75,6 @@ struct Settings }; static const Option CDOC2_DEFAULT; - static const Option CDOC2_NOTIFICATION; static const Option CDOC2_USE_KEYSERVER; static const Option CDOC2_DEFAULT_KEYSERVER; static const Option CDOC2_UUID; From ab18ed81d29edb239342aa0e762f270c965ab888 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Tue, 28 Apr 2026 16:15:25 +0300 Subject: [PATCH 12/15] Fixed translations etc. --- client/Application.cpp | 1 + client/CMakeLists.txt | 2 +- client/CryptoDoc.cpp | 6 ++-- client/QCryptoBackend.cpp | 4 +-- client/Settings.cpp | 2 ++ client/Settings.h | 2 ++ client/translations/en.ts | 59 +++++++++++++++------------------------ client/translations/et.ts | 59 +++++++++++++++------------------------ 8 files changed, 57 insertions(+), 78 deletions(-) diff --git a/client/Application.cpp b/client/Application.cpp index 7a20bc2cf..333bef813 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -433,6 +433,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/CMakeLists.txt b/client/CMakeLists.txt index 475f57341..8c29e3762 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -89,7 +89,7 @@ qt_add_translations(${PROJECT_NAME} TS_FILES translations/et.ts ../common/translations/qtbase_et.ts RESOURCE_PREFIX /translations - LUPDATE_OPTIONS -locations none + LUPDATE_OPTIONS -locations none -no-obsolete ) qt_add_resources(${PROJECT_NAME} tsl BASE ${TSL_DIR} PREFIX /TSL FILES ${TSL_LIST}) qt_add_resources(${PROJECT_NAME} fonts BASE fonts PREFIX /fonts FILES diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 893f8f038..babdfebfa 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -373,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/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index c376481f7..9d9b5de1c 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -67,9 +67,9 @@ QString QCryptoBackend::errorString(Status error) switch( error ) { case PinOK: return QString(); - case PinCanceled: return QCoreApplication::translate("QCryptoBackend", "PIN Canceled"); + 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 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"); diff --git a/client/Settings.cpp b/client/Settings.cpp index 0a3e3e767..7952cd169 100644 --- a/client/Settings.cpp +++ b/client/Settings.cpp @@ -30,6 +30,8 @@ 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 b2355c41f..b5d0af5dc 100644 --- a/client/Settings.h +++ b/client/Settings.h @@ -75,6 +75,8 @@ 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; static const Option CDOC2_UUID; diff --git a/client/translations/en.ts b/client/translations/en.ts index db28da8f2..ef7d1e9e1 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -241,6 +241,13 @@ Start downloading + + C + + DigiDoc4 Client + DigiDoc4 Client + + CDocumentModel @@ -489,14 +496,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! @@ -576,14 +575,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. @@ -600,10 +591,6 @@ Failed to open document Failed to open document - - Don't show again - Don't show again - Wrong password. Wrong password. @@ -2120,18 +2107,10 @@ ID-Card QCryptoBackend - - PIN Canceled - PIN Canceled - PIN locked PIN locked - - PIN Incorrect - PIN Incorrect - PKCS11 general error PKCS11 general error @@ -2144,6 +2123,14 @@ ID-Card Unknown error Unknown error + + PIN incorrect + PIN Incorrect + + + PIN entry canceled + PIN entry canceled + QFileDialog @@ -2154,10 +2141,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. @@ -2178,10 +2161,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 @@ -3047,6 +3026,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 1e9b3e11d..86186dc21 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -241,6 +241,13 @@ Alusta paigaldust + + C + + DigiDoc4 Client + DigiDoc4 klient + + CDocumentModel @@ -489,14 +496,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! @@ -576,14 +575,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. @@ -600,10 +591,6 @@ Failed to open document Dokumendi avamine ebaõnnestus - - Don't show again - Ära rohkem näita - Wrong password. Vale parool. @@ -2120,18 +2107,10 @@ ID-kaardiga QCryptoBackend - - PIN Canceled - PIN katkestatud - PIN locked PIN on lukus - - PIN Incorrect - Vale PIN - PKCS11 general error PKCS11 üldine viga @@ -2144,6 +2123,14 @@ ID-kaardiga Unknown error Tundmatu viga + + PIN incorrect + Vale PIN + + + PIN entry canceled + PIN sisestus katkestatus + QFileDialog @@ -2154,10 +2141,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. @@ -2178,10 +2161,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 @@ -3047,6 +3026,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 From dd0a1a8aef0eab083efedecac4782d6defa38167 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 29 Apr 2026 10:26:23 +0300 Subject: [PATCH 13/15] Add nack Diagnostics translations --- client/CMakeLists.txt | 4 ++-- client/translations/en.ts | 7 ------- client/translations/et.ts | 7 ------- client/widgets/ContainerPage.cpp | 2 +- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 8c29e3762..75641132c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -89,7 +89,7 @@ qt_add_translations(${PROJECT_NAME} TS_FILES translations/et.ts ../common/translations/qtbase_et.ts RESOURCE_PREFIX /translations - LUPDATE_OPTIONS -locations none -no-obsolete + LUPDATE_OPTIONS -locations none ) qt_add_resources(${PROJECT_NAME} tsl BASE ${TSL_DIR} PREFIX /TSL FILES ${TSL_LIST}) qt_add_resources(${PROJECT_NAME} fonts BASE fonts PREFIX /fonts FILES @@ -194,7 +194,7 @@ if( APPLE ) COMMAND zip -r ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-dbg_${VERSION}$ENV{VER_SUFFIX}.zip ${PROJECT_NAME}.dSYM ) elseif(WIN32) - target_sources(${PROJECT_NAME} PRIVATE QCNG.cpp QCNG.h) + target_sources(${PROJECT_NAME} PRIVATE QCNG.cpp QCNG.h Diagnostics_win.cpp) target_compile_options(${PROJECT_NAME} PRIVATE "/guard:cf") target_link_options(${PROJECT_NAME} PRIVATE "/guard:cf" $<$:/INTEGRITYCHECK>) target_link_libraries(${PROJECT_NAME} NCrypt Crypt32 Cryptui winscard) diff --git a/client/translations/en.ts b/client/translations/en.ts index ef7d1e9e1..2ca78bfd5 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -241,13 +241,6 @@ Start downloading - - C - - DigiDoc4 Client - DigiDoc4 Client - - CDocumentModel diff --git a/client/translations/et.ts b/client/translations/et.ts index 86186dc21..fc84c46e2 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -241,13 +241,6 @@ Alusta paigaldust - - C - - DigiDoc4 Client - DigiDoc4 klient - - CDocumentModel diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 289eb2a6d..d5bbb4fa8 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); From c3307cb18a7ebf5418a484570b36d9416ea4ce8a Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Wed, 29 Apr 2026 11:08:02 +0300 Subject: [PATCH 14/15] Update client/CMakeLists.txt --- client/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 75641132c..475f57341 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -194,7 +194,7 @@ if( APPLE ) COMMAND zip -r ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-dbg_${VERSION}$ENV{VER_SUFFIX}.zip ${PROJECT_NAME}.dSYM ) elseif(WIN32) - target_sources(${PROJECT_NAME} PRIVATE QCNG.cpp QCNG.h Diagnostics_win.cpp) + target_sources(${PROJECT_NAME} PRIVATE QCNG.cpp QCNG.h) target_compile_options(${PROJECT_NAME} PRIVATE "/guard:cf") target_link_options(${PROJECT_NAME} PRIVATE "/guard:cf" $<$:/INTEGRITYCHECK>) target_link_libraries(${PROJECT_NAME} NCrypt Crypt32 Cryptui winscard) From ea753701a956f10b9d6c12ba71d42500ff6df8c7 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 1 Jun 2026 14:44:25 +0300 Subject: [PATCH 15/15] Fix translations and code review Signed-off-by: Raul Metsma --- client/CDocSupport.cpp | 24 +-- client/QCNG.cpp | 28 +--- client/QCNG.h | 5 +- client/QCryptoBackend.cpp | 3 +- client/QCryptoBackend.h | 3 +- client/QPKCS11.cpp | 332 +++++++++++++++++++------------------- client/QPKCS11.h | 10 +- client/QSigner.cpp | 26 +-- client/QSigner.h | 1 - client/translations/en.ts | 16 +- client/translations/et.ts | 16 +- 11 files changed, 227 insertions(+), 237 deletions(-) diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 5fc485072..ec07b781f 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -109,19 +109,27 @@ getDecryptStatus(QCryptoBackend::Status pin_status) } } +static libcdoc::result_t +getDecryptResultStatus(const std::vector &result, std::unique_ptr backend) +{ + if (!result.empty()) + return libcdoc::OK; + libcdoc::result_t mapped = getDecryptStatus(backend->status); + return mapped == libcdoc::OK ? DDCryptoBackend::BACKEND_ERROR : mapped; +} + libcdoc::result_t DDCryptoBackend::decryptRSA(std::vector& dst, const std::vector &data, bool oaep, unsigned int idx) { if (!backend) { auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); - if (!val.value()) + if (!val) 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); + return getDecryptResultStatus(dst, std::move(backend)); } libcdoc::result_t @@ -135,15 +143,14 @@ DDCryptoBackend::deriveConcatKDF(std::vector& dst, const std::vectorsigner()->tokenauth()); - if (!val.value()) + if (!val) 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()); - backend.reset(); - return (dst.empty() ? BACKEND_ERROR : libcdoc::OK); + return getDecryptResultStatus(dst, std::move(backend)); } libcdoc::result_t @@ -151,14 +158,13 @@ DDCryptoBackend::deriveHMACExtract(std::vector& dst, const std::vector< { if (!backend) { auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth()); - if (!val.value()) + if (!val) 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); + return getDecryptResultStatus(dst, std::move(backend)); } libcdoc::result_t diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 90ee32437..51f9abf76 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -48,13 +48,9 @@ struct QCNG::Private bool pss; }; -QCNG::QCNG() -{ -} +QCNG::QCNG() noexcept = default; -QCNG::~QCNG() -{ -} +QCNG::~QCNG() noexcept = default; QCNG::Status QCNG::login(const TokenData &token) { @@ -67,20 +63,12 @@ QCNG::Status QCNG::login(const TokenData &token) // 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()); + d = std::move(p); 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; @@ -102,8 +90,6 @@ 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) @@ -128,8 +114,6 @@ 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())}, @@ -160,8 +144,6 @@ 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())}, @@ -182,6 +164,8 @@ QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray template QByteArray QCNG::exec(F &&func) const { + if (!d) + return {}; status = UnknownError; QByteArray result; switch(func(d->prov, d->key, result)) @@ -293,8 +277,6 @@ QList QCNG::tokens() 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) diff --git a/client/QCNG.h b/client/QCNG.h index c79e5f155..7b060e9e7 100644 --- a/client/QCNG.h +++ b/client/QCNG.h @@ -28,11 +28,10 @@ class QCNG final: public QCryptoBackend { public: - explicit QCNG(); - ~QCNG() final; + explicit QCNG() noexcept; + ~QCNG() noexcept 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, diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index 9d9b5de1c..dd523d2f9 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -27,7 +27,6 @@ #include #include -#include // TODO: Port everything to the new OpenSSL API #define OPENSSL_SUPPRESS_DEPRECATED @@ -43,7 +42,7 @@ QCryptoBackend::getBackend(const TokenData& token) { #else auto backend = std::make_unique(); #endif - backend.get()->cert = token.cert(); + backend->cert = token.cert(); Status status; do { status = backend->login(token); diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index a9cc5e13c..9f60ce469 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -19,12 +19,12 @@ #pragma once -#include #include #include class TokenData; +class QSslKey; class QCryptoBackend { @@ -83,7 +83,6 @@ class QCryptoBackend 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 4c9b785c2..4adf9c0ea 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -39,6 +39,7 @@ #include #include +#include template static QString toQString(const Container &c) @@ -46,90 +47,138 @@ static QString toQString(const Container &c) return QString::fromLatin1((const char*)std::data(c), std::size(c)); } -struct QPKCS11Private +struct QPKCS11Library { - static bool load(const QString &driver); - static void unload(); - static void logout(); + explicit QPKCS11Library(const QString &driver); + ~QPKCS11Library(); + 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; + static std::shared_ptr current(); QLibrary lib; - CK_FUNCTION_LIST_PTR f = nullptr; + CK_FUNCTION_LIST_PTR f {}; bool isFinDriver = false; - CK_SESSION_HANDLE session = 0; - QByteArray id; - bool isPSS = false; }; -static QPKCS11Private priv; - -static QPKCS11Private *getPrivate() +QPKCS11Library::QPKCS11Library(const QString &driver) + : lib(driver) { - 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) + if(auto l = CK_C_GetFunctionList(lib.resolve("C_GetFunctionList")); + !l || l(&f) != CKR_OK) { - qWarning() << "Failed to resolve symbols" << d->lib.errorString(); - return false; + qWarning() << "Failed to resolve symbols" << lib.errorString(); + return; } 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 ) + CK_RV err = f->C_Initialize(&init_args); + if(err != CKR_OK && err != CKR_CRYPTOKI_ALREADY_INITIALIZED) { qWarning() << "Failed to initalize"; - return false; + f = nullptr; + return; } CK_INFO info{}; - d->f->C_GetInfo( &info ); + 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; + isFinDriver = toQString(info.libraryDescription).contains(QLatin1String("MPOLLUX"), Qt::CaseInsensitive); } -void -QPKCS11Private::unload() +QPKCS11Library::~QPKCS11Library() { - logout(); - QPKCS11Private *d = getPrivate(); - if(d->f) - d->f->C_Finalize(nullptr); - d->f = nullptr; - d->lib.unload(); + if(f) + f->C_Finalize(nullptr); } -void -QPKCS11Private::logout() +static std::shared_ptr loadLibrary(const QString &driver) { - QPKCS11Private *d = getPrivate(); - d->id.clear(); - if (d->f && d->session) { - d->f->C_Logout(d->session); - d->f->C_CloseSession(d->session); + static std::mutex loadedMutex; + static std::shared_ptr loaded; + std::lock_guard lock(loadedMutex); + if(loaded && loaded->lib.fileName() == driver) + return loaded; + + auto lib = std::make_shared(driver); + if(!lib->f) + return {}; + loaded = lib; + return lib; +} + +std::shared_ptr QPKCS11Library::current() +{ + static const QMultiHash drivers { +#ifdef Q_OS_MAC + { QApplication::applicationDirPath() + "/opensc-pkcs11.so", {} }, + { "/Library/latvia-eid/lib/eidlv-pkcs11.bundle/Contents/MacOS/eidlv-pkcs11", "3BDB960080B1FE451F830012428F536549440F900020" }, // LV-G2 + { "/Library/latvia-eid/lib/eidlv-pkcs11.bundle/Contents/MacOS/eidlv-pkcs11", "3BDC960080B1FE451F830012428F54654944320F900012" }, // LV-G2.1 + { "/Library/mCard/lib/mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020604D1" }, // LT MaskTech 2.6.4 + { "/Library/mCard/lib/mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020605D0" }, // LT MaskTech 2.6.5 + { "/Library/Atostek ID/Atostek-ID-PKCS11.dylib", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 + { "/Library/Atostek ID/Atostek-ID-PKCS11.dylib", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 + { "/Library/Atostek ID/Atostek-ID-PKCS11.dylib", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 + { "/Library/mPolluxDigiSign/libcryptoki.dylib", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 + { "/Library/mPolluxDigiSign/libcryptoki.dylib", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 + { "/Library/mPolluxDigiSign/libcryptoki.dylib", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 + { "/Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib", "3BD5180081313A7D8073C8211030" }, + { "/Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib", "3BD518008131FE7D8073C82110F4" }, + { "/Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib", "3BFF9600008131FE4380318065B0846566FB12017882900085" }, + { "/Library/Frameworks/eToken.framework/Versions/Current/libIDPrimePKCS11.dylib", "3BFF9600008131804380318065B0850300EF120FFE82900066" }, + { "/Library/Frameworks/eToken.framework/Versions/Current/libIDPrimePKCS11.dylib", "3BFF9600008131FE4380318065B0855956FB120FFE82900000" }, +#elif defined(Q_OS_WIN) + { "opensc-pkcs11.dll", {} }, +#else + { "opensc-pkcs11.so", {} }, +#if defined(Q_OS_LINUX) + { "/opt/latvia-eid/lib/eidlv-pkcs11.so", "3BDB960080B1FE451F830012428F536549440F900020" }, // LV-G2 + { "/opt/latvia-eid/lib/eidlv-pkcs11.so", "3BDC960080B1FE451F830012428F54654944320F900012" }, // LV-G2.1 + { "mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020604D1" }, // LT MaskTech 2.6.4 + { "mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020605D0" }, // LT MaskTech 2.6.5 +#if Q_PROCESSOR_WORDSIZE == 8 + { "/usr/lib/Atostek-ID-PKCS11.so", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 + { "/usr/lib/Atostek-ID-PKCS11.so", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 + { "/usr/lib/Atostek-ID-PKCS11.so", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 + { "/usr/lib64/libcryptoki.so", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 + { "/usr/lib64/libcryptoki.so", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 + { "/usr/lib64/libcryptoki.so", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 +#else + { "libcryptoki.so", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 + { "libcryptoki.so", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 + { "libcryptoki.so", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 +#endif + { "/usr/lib/libeTPkcs11.so", "3BD5180081313A7D8073C8211030" }, + { "/usr/lib/libeTPkcs11.so", "3BD518008131FE7D8073C82110F4" }, + { "/usr/lib/libeTPkcs11.so", "3BFF9600008131FE4380318065B0846566FB12017882900085" }, + { "/usr/lib/libIDPrimePKCS11.so", "3BFF9600008131804380318065B0850300EF120FFE82900066" }, + { "/usr/lib/libIDPrimePKCS11.so", "3BFF9600008131FE4380318065B0855956FB120FFE82900000" }, +#endif +#endif + }; + for(const QString &reader: QPCSC::instance().readers()) + { + QPCSCReader r(reader, &QPCSC::instance()); + if(!r.isPresent()) + continue; + QByteArray atr = r.atr(); + for(auto i = drivers.cbegin(); i != drivers.cend(); ++i) { + if(i.value() == atr) { + if(auto lib = loadLibrary(i.key())) + return lib; + } + } } - d->session = 0; + return loadLibrary(drivers.key({})); } -QByteArray -QPKCS11Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const +QByteArray QPKCS11Library::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const { QByteArray data; CK_ATTRIBUTE attr { type, nullptr, 0 }; @@ -142,8 +191,7 @@ QPKCS11Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_AT return data; } -std::vector -QPKCS11Private::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id) const +std::vector QPKCS11Library::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id) const { std::vector result; if(!f) @@ -168,21 +216,39 @@ QPKCS11Private::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const return result; } + +struct QPKCS11::Private +{ + void closeSession() + { + id.clear(); + if(l && l->f && session) { + l->f->C_Logout(session); + l->f->C_CloseSession(session); + } + session = 0; + } + + std::shared_ptr l; + CK_SESSION_HANDLE session = 0; + QByteArray id; + bool isPSS = false; +}; + QPKCS11::QPKCS11() : QCryptoBackend() + , d(std::make_unique()) { } -QPKCS11::~QPKCS11() +QPKCS11::~QPKCS11() noexcept { - logout(); + d->closeSession(); } -QByteArray -QPKCS11::decrypt(const QByteArray &data, bool oaep) const +QByteArray QPKCS11::decrypt(const QByteArray &data, bool oaep) const { - QPKCS11Private *d = getPrivate(); - auto key = d->findObject(d->session, CKO_PRIVATE_KEY, d->id); + auto key = d->l->findObject(d->session, CKO_PRIVATE_KEY, d->id); if(key.size() != 1){ status = GeneralError; return {}; @@ -192,18 +258,18 @@ QPKCS11::decrypt(const QByteArray &data, bool oaep) const 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->l->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->l->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->l->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 {}; } @@ -211,11 +277,9 @@ QPKCS11::decrypt(const QByteArray &data, bool oaep) const 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); + std::vector key = d->l->findObject(d->session, CKO_PRIVATE_KEY, d->id); if(key.size() != 1) { status = GeneralError; return {}; @@ -228,25 +292,24 @@ QPKCS11::derive(const QByteArray &publicKey) const CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; CK_ULONG value_len = (publicKey.size() - 1) / 2; - std::array newkey_template{ - CK_ATTRIBUTE{CKA_TOKEN, &_false, sizeof(_false)}, - CK_ATTRIBUTE{CKA_CLASS, &newkey_class, sizeof(newkey_class)}, - CK_ATTRIBUTE{CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, - CK_ATTRIBUTE{CKA_SENSITIVE, &_false, sizeof(_false)}, - CK_ATTRIBUTE{CKA_EXTRACTABLE, &_true, sizeof(_true)}, - CK_ATTRIBUTE{CKA_VALUE_LEN, &value_len, sizeof(value_len)}, - }; + auto newkey_template = std::to_array({ + {CKA_TOKEN, &_false, sizeof(_false)}, + {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, + {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, + {CKA_SENSITIVE, &_false, sizeof(_false)}, + {CKA_EXTRACTABLE, &_true, sizeof(_true)}, + {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->l->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); + return d->l->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); @@ -270,8 +333,7 @@ QPKCS11::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash::Algori 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()) @@ -300,27 +362,30 @@ QPKCS11::deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, return out; } -QPKCS11::Status -QPKCS11::login(const TokenData &t) +QPKCS11::Status QPKCS11::login(const TokenData &t) { - logout(); + if(!d->l) { + d->l = QPKCS11Library::current(); + } + if(!d->l || !d->l->f) + return UnknownError; - QPKCS11Private *d = getPrivate(); + d->closeSession(); auto currentSlot = t.data(QStringLiteral("slot")).value(); d->id = t.data(QStringLiteral("id")).toByteArray(); d->isPSS = t.data(QStringLiteral("PSS")).toBool(); CK_TOKEN_INFO token; - if(d->f->C_GetTokenInfo(currentSlot, &token) != CKR_OK || - d->f->C_OpenSession(currentSlot, CKF_SERIAL_SESSION, nullptr, nullptr, &d->session) != CKR_OK) + if(d->l->f->C_GetTokenInfo(currentSlot, &token) != CKR_OK || + d->l->f->C_OpenSession(currentSlot, CKF_SERIAL_SESSION, nullptr, nullptr, &d->session) != CKR_OK) return UnknownError; - std::vector list = d->findObject(d->session, CKO_CERTIFICATE, d->id); - if(list.size() != 1 || QSslCertificate(d->attribute(d->session, list.front(), CKA_VALUE), QSsl::Der) != t.cert()) + std::vector list = d->l->findObject(d->session, CKO_CERTIFICATE, d->id); + if(list.size() != 1 || QSslCertificate(d->l->attribute(d->session, list.front(), CKA_VALUE), QSsl::Der) != t.cert()) return UnknownError; // Hack: Workaround broken FIN pkcs11 drivers not providing CKF_LOGIN_REQUIRED info - if(!d->isFinDriver && !(token.flags & CKF_LOGIN_REQUIRED)) + if(!d->l->isFinDriver && !(token.flags & CKF_LOGIN_REQUIRED)) return PinOK; SslCertificate cert(t.cert()); @@ -335,14 +400,14 @@ QPKCS11::login(const TokenData &t) PinPopup p(isSign ? QSmartCardData::Pin2Type : QSmartCardData::Pin1Type, f, cert, Application::mainWindow()); p.open(); p.startTimer(); - return waitFor(d->f->C_Login, d->session, CKU_USER, nullptr, 0); + return waitFor(d->l->f->C_Login, d->session, CKU_USER, nullptr, 0); } else { PinPopup p(isSign ? QSmartCardData::Pin2Type : QSmartCardData::Pin1Type, f, cert, Application::mainWindow()); p.setPinLen(token.ulMinPinLen, token.ulMaxPinLen < 12 ? 12 : token.ulMaxPinLen); if(!p.exec()) return CKR_FUNCTION_CANCELED; QByteArray pin = p.pin().toUtf8(); - return d->f->C_Login(d->session, CKU_USER, CK_UTF8CHAR_PTR(pin.constData()), CK_ULONG(pin.size())); + return d->l->f->C_Login(d->session, CKU_USER, CK_UTF8CHAR_PTR(pin.constData()), CK_ULONG(pin.size())); } }); @@ -355,7 +420,7 @@ QPKCS11::login(const TokenData &t) case CKR_FUNCTION_CANCELED: return PinCanceled; case CKR_PIN_INCORRECT: - d->f->C_GetTokenInfo(currentSlot, &token); + d->l->f->C_GetTokenInfo(currentSlot, &token); return (token.flags & CKF_USER_PIN_LOCKED) ? PinLocked : PinIncorrect; case CKR_PIN_LOCKED: return PinLocked; @@ -368,18 +433,11 @@ QPKCS11::login(const TokenData &t) } } -void QPKCS11::logout() -{ - QPKCS11Private *d = getPrivate(); - d->logout(); -} - QList QPKCS11::tokens() { QList list; - reload(); - QPKCS11Private *d = getPrivate(); - if(!d->f) + auto d = QPKCS11Library::current(); + if(!d || !d->f) return list; size_t size = 0; if(d->f->C_GetSlotList(CK_TRUE, nullptr, CK_ULONG_PTR(&size)) != CKR_OK) @@ -434,73 +492,9 @@ QList QPKCS11::tokens() return list; } -bool QPKCS11::reload() -{ - static const QMultiHash drivers { -#ifdef Q_OS_MAC - { QApplication::applicationDirPath() + "/opensc-pkcs11.so", {} }, - { "/Library/latvia-eid/lib/eidlv-pkcs11.bundle/Contents/MacOS/eidlv-pkcs11", "3BDB960080B1FE451F830012428F536549440F900020" }, // LV-G2 - { "/Library/latvia-eid/lib/eidlv-pkcs11.bundle/Contents/MacOS/eidlv-pkcs11", "3BDC960080B1FE451F830012428F54654944320F900012" }, // LV-G2.1 - { "/Library/mCard/lib/mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020604D1" }, // LT MaskTech 2.6.4 - { "/Library/mCard/lib/mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020605D0" }, // LT MaskTech 2.6.5 - { "/Library/Atostek ID/Atostek-ID-PKCS11.dylib", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 - { "/Library/Atostek ID/Atostek-ID-PKCS11.dylib", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 - { "/Library/Atostek ID/Atostek-ID-PKCS11.dylib", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 - { "/Library/mPolluxDigiSign/libcryptoki.dylib", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 - { "/Library/mPolluxDigiSign/libcryptoki.dylib", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 - { "/Library/mPolluxDigiSign/libcryptoki.dylib", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 - { "/Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib", "3BD5180081313A7D8073C8211030" }, - { "/Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib", "3BD518008131FE7D8073C82110F4" }, - { "/Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib", "3BFF9600008131FE4380318065B0846566FB12017882900085" }, - { "/Library/Frameworks/eToken.framework/Versions/Current/libIDPrimePKCS11.dylib", "3BFF9600008131804380318065B0850300EF120FFE82900066" }, - { "/Library/Frameworks/eToken.framework/Versions/Current/libIDPrimePKCS11.dylib", "3BFF9600008131FE4380318065B0855956FB120FFE82900000" }, -#elif defined(Q_OS_WIN) - { "opensc-pkcs11.dll", {} }, -#else - { "opensc-pkcs11.so", {} }, -#if defined(Q_OS_LINUX) - { "/opt/latvia-eid/lib/eidlv-pkcs11.so", "3BDB960080B1FE451F830012428F536549440F900020" }, // LV-G2 - { "/opt/latvia-eid/lib/eidlv-pkcs11.so", "3BDC960080B1FE451F830012428F54654944320F900012" }, // LV-G2.1 - { "mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020604D1" }, // LT MaskTech 2.6.4 - { "mcard-pkcs11.so", "3B9D188131FC358031C0694D54434F5373020605D0" }, // LT MaskTech 2.6.5 -#if Q_PROCESSOR_WORDSIZE == 8 - { "/usr/lib/Atostek-ID-PKCS11.so", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 - { "/usr/lib/Atostek-ID-PKCS11.so", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 - { "/usr/lib/Atostek-ID-PKCS11.so", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 - { "/usr/lib64/libcryptoki.so", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 - { "/usr/lib64/libcryptoki.so", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 - { "/usr/lib64/libcryptoki.so", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 -#else - { "libcryptoki.so", "3B7F9600008031B865B08504021B1200F6829000" }, // FI-G3.1 - { "libcryptoki.so", "3B7F9600008031B865B085050011122460829000" }, // FI-G4 - { "libcryptoki.so", "3B7F9600008031B865B085051024122460829000" }, // FI-G4.1 -#endif - { "/usr/lib/libeTPkcs11.so", "3BD5180081313A7D8073C8211030" }, - { "/usr/lib/libeTPkcs11.so", "3BD518008131FE7D8073C82110F4" }, - { "/usr/lib/libeTPkcs11.so", "3BFF9600008131FE4380318065B0846566FB12017882900085" }, - { "/usr/lib/libIDPrimePKCS11.so", "3BFF9600008131804380318065B0850300EF120FFE82900066" }, - { "/usr/lib/libIDPrimePKCS11.so", "3BFF9600008131FE4380318065B0855956FB120FFE82900000" }, -#endif -#endif - }; - for(const QString &reader: QPCSC::instance().readers()) - { - QPCSCReader r(reader, &QPCSC::instance()); - if(!r.isPresent()) - continue; - QByteArray atr = r.atr(); - for(auto i = drivers.cbegin(); i != drivers.cend(); ++i) { - if(i.value() == atr && QPKCS11Private::load(i.key())) - return true; - } - } - 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); + std::vector key = d->l->findObject(d->session, CKO_PRIVATE_KEY, d->id); if(key.size() != 1) { status = GeneralError; return {}; @@ -508,7 +502,7 @@ QByteArray QPKCS11::sign(QCryptographicHash::Algorithm type, const QByteArray &d CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE attribute { CKA_KEY_TYPE, &keyType, sizeof(keyType) }; - d->f->C_GetAttributeValue(d->session, key.front(), &attribute, 1); + d->l->f->C_GetAttributeValue(d->session, key.front(), &attribute, 1); CK_RSA_PKCS_PSS_PARAMS pssParams { CKM_SHA256, CKG_MGF1_SHA256, 32 }; CK_MECHANISM mech { keyType == CKK_ECDSA ? CKM_ECDSA : CKM_RSA_PKCS, nullptr, 0 }; @@ -543,17 +537,17 @@ 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->l->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->l->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->l->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 {}; } diff --git a/client/QPKCS11.h b/client/QPKCS11.h index fbc80e10b..c01994018 100644 --- a/client/QPKCS11.h +++ b/client/QPKCS11.h @@ -21,11 +21,13 @@ #include "QCryptoBackend.h" +#include + class QPKCS11 final: public QCryptoBackend { public: explicit QPKCS11(); - ~QPKCS11() final; + ~QPKCS11() noexcept final; QByteArray decrypt(const QByteArray &data, bool oaep) const final; QByteArray derive(const QByteArray &publicKey) const; @@ -35,8 +37,10 @@ class QPKCS11 final: public QCryptoBackend QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final; Status login(const TokenData &t) final; - void logout() final; - static bool reload(); static QList tokens(); + +private: + struct Private; + std::unique_ptr d; }; diff --git a/client/QSigner.cpp b/client/QSigner.cpp index 790ce23e4..ee960d727 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -112,13 +112,6 @@ X509Cert QSigner::cert() const return X509Cert((const unsigned char*)der.constData(), size_t(der.size()), X509Cert::Der); } -void QSigner::logout() const -{ - d->lock.unlock(); - // QSmartCard should also know that PIN1 info is updated - d->smartcard->reloadCard(d->smartcard->tokenData(), true); -} - QCryptographicHash::Algorithm QSigner::methodToNID(const std::string &method) { if(method == "http://www.w3.org/2001/04/xmldsig-more#sha224" || @@ -243,8 +236,14 @@ std::vector QSigner::sign(const std::string &method, const std::v throwException(tr("Signing certificate is not selected."), Exception::General) } + auto unlock = qScopeGuard([this] { + d->lock.unlock(); + // QSmartCard should also know that PIN2 info is updated + d->smartcard->reloadCard(d->smartcard->tokenData(), true); + }); + auto val = QCryptoBackend::getBackend(d->sign); - if (!val.value()) { + if (!val) { switch(val.error()) { case QCryptoBackend::PinCanceled: throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(val.error())), Exception::PINCanceled); @@ -258,8 +257,17 @@ std::vector QSigner::sign(const std::string &method, const std::v QByteArray sig = waitFor(&QCryptoBackend::sign, backend.get(), methodToNID(method), QByteArray::fromRawData((const char*)digest.data(), int(digest.size()))); - if (sig.isEmpty()) + if (sig.isEmpty()) { + switch(backend->status) { + case QCryptoBackend::PinCanceled: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINCanceled); + case QCryptoBackend::PinLocked: + throwException((tr("Failed to login token") + ' ' + QCryptoBackend::errorString(backend->status)), Exception::PINLocked) + default: + break; + } throwException(tr("Failed to sign document"), Exception::General) + } return {sig.constBegin(), sig.constEnd()}; } diff --git a/client/QSigner.h b/client/QSigner.h index 92cc91a6a..8a08150ea 100644 --- a/client/QSigner.h +++ b/client/QSigner.h @@ -41,7 +41,6 @@ class QSigner final: public QThread, public digidoc::Signer QList cache() const; digidoc::X509Cert cert() const final; - void logout() const; void selectCard(const TokenData &token); std::vector sign( const std::string &method, const std::vector &digest) const final; diff --git a/client/translations/en.ts b/client/translations/en.ts index 2ca78bfd5..4638247d4 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -489,6 +489,14 @@ 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! @@ -3019,14 +3027,6 @@ 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 fc84c46e2..85d873a0f 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -489,6 +489,14 @@ 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! @@ -3019,14 +3027,6 @@ 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