From cb31379bb755b0f15863ecbfdeae5cddc9b7da46 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 | 209 +++++++++++++++++++++++--------------- client/QPKCS11.h | 14 +-- client/QPKCS11_p.h | 43 -------- client/QSigner.cpp | 147 ++------------------------- client/QSigner.h | 2 - 8 files changed, 368 insertions(+), 314 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 f8c127d45..1928792dc 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 {}; @@ -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 468e7e23b..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: - class Private; - Private *d; + + static QList tokens(); }; diff --git a/client/QPKCS11_p.h b/client/QPKCS11_p.h deleted file mode 100644 index c80e6b21f..000000000 --- a/client/QPKCS11_p.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include "QPKCS11.h" - -#include "pkcs11.h" - -#include - -#include - -class QPKCS11::Private: public QObject -{ - Q_OBJECT -public: - QByteArray attribute( CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type ) const; - std::vector findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const QByteArray &id = {}) const; - - QLibrary lib; - CK_FUNCTION_LIST_PTR f = nullptr; - bool isFinDriver = false; - CK_SESSION_HANDLE session = 0; - QByteArray id; - bool isPSS = false; -}; diff --git a/client/QSigner.cpp b/client/QSigner.cpp index e8328e74a..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 d5cac43be5f69d2236efeed9512dad2e363e31ac 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 1928792dc..0f7a18648 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 5b4494cacda936bef39b90561355f0666a9e6b84 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 0f7a18648..2ad411c92 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 940727f8513d3f0b5aeb985a134f720d9ae22162 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 2ad411c92..028dafe08 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 4a431cacfb59751867166c28172d28e8407b70bb 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 d8de3b09c..2da592074 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 028dafe08..918aaa20c 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 513cabf16afb8313429c480385d4f03b43677de0 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Wed, 22 Apr 2026 14:03:58 +0300 Subject: [PATCH 06/15] Remove extra status --- client/QCNG.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 0e0d4c8ec..cd99d13b7 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) { - 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 f5d5aff9b70a714254877986ab3eac42b0bf7ec8 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 22 Apr 2026 15:24:56 +0300 Subject: [PATCH 07/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 918aaa20c..431d667f0 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 a4011ce63bf688ab4be37db93533fc83ec2065e8 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 53ac9652397c04dda0390e267a18d2fc9889966f 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 4547a5015b7807a714c641b9f121cfd7d751f5b3 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 c1fe2a90a..48042d5eb 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 9bb332994b6ba050b7fc974afa140eefa7ad3a01 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 2da592074..b5aaba2c4 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 54a7743bc..82b2b60e2 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 febac3bb07cdcd376017976d9d44827038e0140d 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 | 83 +++++++++++---------------------------- client/translations/et.ts | 83 +++++++++++---------------------------- client/translations/ru.ts | 83 +++++++++++---------------------------- 9 files changed, 80 insertions(+), 186 deletions(-) diff --git a/client/Application.cpp b/client/Application.cpp index 48042d5eb..0a73e1d6c 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -432,6 +432,7 @@ Application::Application( int &argc, char **argv ) // Clear obsolete registriy settings #ifndef Q_OS_DARWIN Settings::DEFAULT_DIR.clear(); + Settings::CDOC2_NOTIFICATION.clear(); #endif // Actions diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a7767244c..89fb16259 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -89,7 +89,7 @@ qt_add_translations(${PROJECT_NAME} TS_FILES ../common/translations/qtbase_et.ts ../common/translations/qtbase_ru.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 b5aaba2c4..ba9b1a477 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 82b2b60e2..a4a16bcb8 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 158c6a678..208cffb1c 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! @@ -580,14 +579,6 @@ Failed to add key Failed to add key - - You must enter your PIN code twice in order to decrypt the CDOC2 container - You must enter your PIN code twice in order to decrypt the CDOC2 container - - - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - Please check your internet connection and network settings. Please check your internet connection and network settings. @@ -604,10 +595,6 @@ Failed to open document Failed to open document - - Don't show again - Don't show again - Wrong password. Wrong password. @@ -695,30 +682,6 @@ USB info: USB info: - - Unknown - error %1 - Unknown - error %1 - - - User - User - - - Administrator - Administrator - - - User rights: - User rights: - - - Not found - Not found - - - Certificate Propagation service status: - Certificate Propagation service status: - is set manually is set manually @@ -2124,18 +2087,10 @@ ID-Card QCryptoBackend - - PIN Canceled - PIN Canceled - PIN locked PIN locked - - PIN Incorrect - PIN Incorrect - PKCS11 general error PKCS11 general error @@ -2148,6 +2103,14 @@ ID-Card Unknown error Unknown error + + PIN incorrect + PIN Incorrect + + + PIN entry canceled + PIN entry canceled + QFileDialog @@ -2158,10 +2121,6 @@ ID-Card QSigner - - Failed to load PKCS#11 module - Failed to load PKCS#11 module - Signing certificate is not selected. Signing certificate is not selected. @@ -2182,10 +2141,6 @@ ID-Card Signing/decrypting is already in progress another window. Signing/decrypting is already in progress another window. - - Authentication certificate is not selected. - Authentication certificate is not selected. - Failed to decrypt document Failed to decrypt document @@ -3051,6 +3006,14 @@ Additional licenses and components Remove Remove + + You are about to delete the last file in the container + You are about to delete the last file in the container + + + It is removed along with the container. + It is removed along with the container. + WarningItem diff --git a/client/translations/et.ts b/client/translations/et.ts index a26b51075..15cb31cf1 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! @@ -580,14 +579,6 @@ Failed to add key Võtme lisamine ebaõnnestus - - You must enter your PIN code twice in order to decrypt the CDOC2 container - CDOC2 ümbriku dekrüpteerimiseks tuleb sisestada PIN-koodi kaks korda - - - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - Esimene PIN-koodi sisestamine on vajalik autentimiseks CDOC2 ümbrikus viidatud võtmeedastusserverisse. Teine PIN-koodi sisestamine on vajalik CDOC2 ümbriku dekrüpteerimiseks. - Please check your internet connection and network settings. Palun kontrolli internetiühendust ja võrgu sätteid. @@ -604,10 +595,6 @@ Failed to open document Dokumendi avamine ebaõnnestus - - Don't show again - Ära rohkem näita - Wrong password. Vale parool. @@ -695,30 +682,6 @@ USB info: USB info: - - Unknown - error %1 - Teadmata - viga %1 - - - User - Kasutaja - - - Administrator - Administraator - - - User rights: - Kasutajaõigused: - - - Not found - Ei leitud - - - Certificate Propagation service status: - Certificate Propagation teenuse staatus: - is set manually käsitsi määratud @@ -2124,18 +2087,10 @@ ID-kaardiga QCryptoBackend - - PIN Canceled - PIN katkestatud - PIN locked PIN on lukus - - PIN Incorrect - Vale PIN - PKCS11 general error PKCS11 üldine viga @@ -2148,6 +2103,14 @@ ID-kaardiga Unknown error Tundmatu viga + + PIN incorrect + Vale PIN + + + PIN entry canceled + PIN sisestus katkestatus + QFileDialog @@ -2158,10 +2121,6 @@ ID-kaardiga QSigner - - Failed to load PKCS#11 module - PKCS#11 mooduli laadimine ebaõnnestus - Signing certificate is not selected. Allkirjastamise sertifikaat ei ole valitud. @@ -2182,10 +2141,6 @@ ID-kaardiga Signing/decrypting is already in progress another window. Allkirjastamine/dekrüpteerimine on juba käimas teises aknas. - - Authentication certificate is not selected. - Isikutuvastamise sertifikaat ei ole valitud. - Failed to decrypt document Dokumendi dekrüpteerimine ebaõnnestus @@ -3051,6 +3006,14 @@ Täiendavad litsentsid ja komponendid Remove Eemalda + + You are about to delete the last file in the container + Oled kustutamas viimast faili ümbrikus + + + It is removed along with the container. + Koos sellega eemaldatakse ka ümbrik. + WarningItem diff --git a/client/translations/ru.ts b/client/translations/ru.ts index 0bf05445e..87ed8a58f 100644 --- a/client/translations/ru.ts +++ b/client/translations/ru.ts @@ -241,6 +241,13 @@ Начать установку + + C + + DigiDoc4 Client + DigiDoc4 клиент + + CDocumentModel @@ -489,14 +496,6 @@ Decrypting Расшифровка - - You are about to delete the last file in the container - Вы собираетесь удалить последний файл в контейнере - - - It is removed along with the container. - Он удаляется вместе с контейнером. - In order to view Validity Confirmation Sheet there has to be at least one printer installed! Для отображения листа подтверждения действительности цифровой подписи на компьютере должен быть установлен как минимум один принтер! @@ -580,14 +579,6 @@ Failed to add key Не удалось добавить ключ - - You must enter your PIN code twice in order to decrypt the CDOC2 container - PIN-код необходимо ввести дважды, чтобы расшифровать конверт CDOC2 - - - The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. Second PIN entry is required to decrypt the CDOC2 container. - Первый ввод PIN-кода требуется для аутентификации на сервере передачи ключей, указанном в конверте CDOC2. Для расшифровки конверта CDOC2 требуется второй ввод PIN-кода. - Please check your internet connection and network settings. Пожалуйста, проверьте подключение к Интернету и настройки сети. @@ -604,10 +595,6 @@ Failed to open document Не удалось открыть документ - - Don't show again - Больше не показывать - Wrong password. Неверный пароль. @@ -695,30 +682,6 @@ USB info: Информация об USB: - - Unknown - error %1 - Неизвестная - ошибка %1 - - - User - Пользователь - - - Administrator - Администратор - - - User rights: - Права пользователя: - - - Not found - Не найдено - - - Certificate Propagation service status: - Статус Certificate Propagation сервиса: - is set manually назначено вручную @@ -2124,18 +2087,10 @@ ID-картой QCryptoBackend - - PIN Canceled - PIN-код отменен - PIN locked PIN заблокирован - - PIN Incorrect - Неверный PIN - PKCS11 general error Ошибка PKCS11 @@ -2148,6 +2103,14 @@ ID-картой Unknown error Неизвестная ошибка + + PIN incorrect + Неверный PIN + + + PIN entry canceled + PIN-код отменен + QFileDialog @@ -2158,10 +2121,6 @@ ID-картой QSigner - - Failed to load PKCS#11 module - Неудачная загрузка модуля PKCS#11 - Signing certificate is not selected. Сертификат подписи не выбран. @@ -2182,10 +2141,6 @@ ID-картой Signing/decrypting is already in progress another window. Операция подписи/расшифровки уже выполняется в другом окне. - - Authentication certificate is not selected. - Сертификат идентификации не выбран. - Failed to decrypt document Не удалось расшифровать документ @@ -3052,6 +3007,14 @@ Additional licenses and components Remove Удалить + + You are about to delete the last file in the container + Вы собираетесь удалить последний файл в контейнере + + + It is removed along with the container. + Он удаляется вместе с контейнером. + WarningItem From 859db74c73e8fb4dc690af4a56b831e00044c999 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 | 31 ++++++++++++++++++++++++------- client/translations/et.ts | 31 ++++++++++++++++++++++++------- client/translations/ru.ts | 31 ++++++++++++++++++++++++------- client/widgets/ContainerPage.cpp | 2 +- 5 files changed, 75 insertions(+), 24 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 89fb16259..6173678db 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -89,7 +89,7 @@ qt_add_translations(${PROJECT_NAME} TS_FILES ../common/translations/qtbase_et.ts ../common/translations/qtbase_ru.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 208cffb1c..8a2c95a8a 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -241,13 +241,6 @@ Start downloading - - C - - DigiDoc4 Client - DigiDoc4 Client - - CDocumentModel @@ -698,6 +691,30 @@ TSL cache TSL cache + + Unknown - error %1 + + + + User + + + + Administrator + + + + User rights: + + + + Not found + + + + Certificate Propagation service status: + + DigiDoc diff --git a/client/translations/et.ts b/client/translations/et.ts index 15cb31cf1..cda5f5a29 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -241,13 +241,6 @@ Alusta paigaldust - - C - - DigiDoc4 Client - DigiDoc4 klient - - CDocumentModel @@ -698,6 +691,30 @@ TSL cache TSL puhver + + Unknown - error %1 + + + + User + + + + Administrator + + + + User rights: + + + + Not found + + + + Certificate Propagation service status: + + DigiDoc diff --git a/client/translations/ru.ts b/client/translations/ru.ts index 87ed8a58f..e163cc895 100644 --- a/client/translations/ru.ts +++ b/client/translations/ru.ts @@ -241,13 +241,6 @@ Начать установку - - C - - DigiDoc4 Client - DigiDoc4 клиент - - CDocumentModel @@ -698,6 +691,30 @@ TSL cache TSL-буфер + + Unknown - error %1 + + + + User + + + + Administrator + + + + User rights: + + + + Not found + + + + Certificate Propagation service status: + + DigiDoc diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index e2c7aeb3a..18d3dab8b 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -232,7 +232,7 @@ void ContainerPage::deleteConfirm(C *c, int index) if (dlg->exec() != QMessageBox::Ok) return; window()->setWindowFilePath({}); - window()->setWindowTitle(tr("DigiDoc4 Client")); + window()->setWindowTitle(QCoreApplication::translate("MainWindow", "DigiDoc4 Client")); if(QFile::exists(c->fileName())) QFile::remove(c->fileName()); emit action(ContainerClose); From a5b06bac1d6b16509703f96bf1eb39700c582c5f Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 29 Apr 2026 10:35:40 +0300 Subject: [PATCH 14/15] Put Diagnostics translations back --- client/translations/en.ts | 40 +++++++++++++++++++-------------------- client/translations/et.ts | 40 +++++++++++++++++++-------------------- client/translations/ru.ts | 40 +++++++++++++++++++-------------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/client/translations/en.ts b/client/translations/en.ts index 8a2c95a8a..291fe505b 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -676,44 +676,44 @@ USB info: - is set manually - is set manually + Unknown - error %1 + Unknown - error %1 - is set by default - is set by default + User + User - TSL signing certs - TSL signing certs + Administrator + Administrator - TSL cache - TSL cache + User rights: + User rights: - Unknown - error %1 - + Not found + Not found - User - + Certificate Propagation service status: + Certificate Propagation service status: - Administrator - + is set manually + is set manually - User rights: - + is set by default + is set by default - Not found - + TSL signing certs + TSL signing certs - Certificate Propagation service status: - + TSL cache + TSL cache diff --git a/client/translations/et.ts b/client/translations/et.ts index cda5f5a29..f485df513 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -676,44 +676,44 @@ USB info: - is set manually - käsitsi määratud + Unknown - error %1 + Teadmata - viga %1 - is set by default - vaikimisi määratud + User + Kasutaja - TSL signing certs - TSL signeerimise sertifikaadid + Administrator + Administraator - TSL cache - TSL puhver + User rights: + Kasutajaõigused: - Unknown - error %1 - + Not found + Ei leitud - User - + Certificate Propagation service status: + Certificate Propagation teenuse staatus: - Administrator - + is set manually + käsitsi määratud - User rights: - + is set by default + vaikimisi määratud - Not found - + TSL signing certs + TSL signeerimise sertifikaadid - Certificate Propagation service status: - + TSL cache + TSL puhver diff --git a/client/translations/ru.ts b/client/translations/ru.ts index e163cc895..0d330f261 100644 --- a/client/translations/ru.ts +++ b/client/translations/ru.ts @@ -676,44 +676,44 @@ Информация об USB: - is set manually - назначено вручную + Unknown - error %1 + Неизвестная - ошибка %1 - is set by default - назначено по умолчанию + User + Пользователь - TSL signing certs - Сертификаты подписывания TSL + Administrator + Администратор - TSL cache - TSL-буфер + User rights: + Права пользователя: - Unknown - error %1 - + Not found + Не найдено - User - + Certificate Propagation service status: + Статус Certificate Propagation сервиса: - Administrator - + is set manually + назначено вручную - User rights: - + is set by default + назначено по умолчанию - Not found - + TSL signing certs + Сертификаты подписывания TSL - Certificate Propagation service status: - + TSL cache + TSL-буфер From f93ec1d04e777cf66b76e1e327eb64872c4b6a48 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Wed, 29 Apr 2026 11:08:02 +0300 Subject: [PATCH 15/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 6173678db..a7767244c 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)