From 746cac7441ab9e937a2a5ee7757126085a1e20f7 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Wed, 11 Feb 2026 11:02:47 +0200 Subject: [PATCH] Show locked card warning IB-8631 Signed-off-by: Raul Metsma --- client/Application.cpp | 3 +- client/MainWindow.cpp | 52 ++++++------- client/MainWindow.h | 16 ++-- client/QSmartCard.cpp | 5 +- client/common_enums.h | 30 -------- client/widgets/ContainerPage.cpp | 5 +- client/widgets/SignatureItem.cpp | 11 +-- client/widgets/SignatureItem.h | 4 +- client/widgets/VerifyCert.cpp | 26 ++++--- client/widgets/VerifyCert.h | 2 +- client/widgets/VerifyCert.ui | 124 +++++++++++++++---------------- client/widgets/WarningItem.cpp | 26 +++++-- client/widgets/WarningItem.h | 29 +++++++- client/widgets/WarningList.cpp | 2 +- 14 files changed, 177 insertions(+), 158 deletions(-) diff --git a/client/Application.cpp b/client/Application.cpp index 553f2dd74..410cabd7f 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -944,10 +944,9 @@ void Application::showClient(QStringList files, bool crypto, bool sign, bool new if(files.isEmpty()) return; QMetaObject::invokeMethod(w, [&] { - using enum ria::qdigidoc4::Pages; if(sign) sign = files.size() != 1 || !CONTAINER_EXT.contains(QFileInfo(files.value(0)).suffix(), Qt::CaseInsensitive); - w->selectPage(crypto && !sign ? CryptoIntro : SignIntro); + w->selectPage(crypto && !sign ? MainWindow::CryptoIntro : MainWindow::SignIntro); w->openFiles(std::move(files), false, sign); }); } diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 50c691af3..c386f8349 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -50,7 +50,7 @@ using namespace std::chrono; MainWindow::MainWindow( QWidget *parent ) : QWidget( parent ) - , ui( new Ui::MainWindow ) + , ui(std::make_unique()) { setAttribute(Qt::WA_DeleteOnClose, true); setAcceptDrops( true ); @@ -125,12 +125,7 @@ MainWindow::MainWindow( QWidget *parent ) updateMyEid(qApp->signer()->smartcard()->data()); } -MainWindow::~MainWindow() -{ - digiDoc.reset(); - cryptoDoc.reset(); - delete ui; -} +MainWindow::~MainWindow() noexcept = default; void MainWindow::adjustDrops() { @@ -709,27 +704,34 @@ void MainWindow::updateMyEid(const QSmartCardData &data) return; bool pin1Blocked = data.retryCount(QSmartCardData::Pin1Type) == 0; bool pin2Blocked = data.retryCount(QSmartCardData::Pin2Type) == 0; + bool pin1Locked = data.pinLocked(QSmartCardData::Pin1Type); bool pin2Locked = data.pinLocked(QSmartCardData::Pin2Type); ui->myEid->warningIcon( - pin1Blocked || + pin1Blocked || pin1Locked || pin2Blocked || pin2Locked || data.retryCount(QSmartCardData::PukType) == 0); ui->signContainerPage->cardChanged(data.signCert(), pin2Blocked || pin2Locked); - ui->cryptoContainerPage->cardChanged(data.authCert(), pin1Blocked); - - if(pin1Blocked) - ui->warnings->showWarning({WarningType::UnblockPin1Warning, 0, - [this]{ changePinClicked(QSmartCardData::Pin1Type, QSmartCard::UnblockWithPuk); }}); - - if(pin2Locked && pin2Blocked) - ui->warnings->showWarning({WarningType::ActivatePin2WithPUKWarning, 0, - [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPuk); }}); - else if(pin2Blocked) - ui->warnings->showWarning({WarningType::UnblockPin2Warning, 0, - [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::UnblockWithPuk); }}); - else if(pin2Locked) - ui->warnings->showWarning({WarningType::ActivatePin2Warning, 0, - [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPin); }}); + ui->cryptoContainerPage->cardChanged(data.authCert(), pin1Blocked || pin1Locked); + + using enum WarningText::WarningType; + if(pin1Locked) + ui->warnings->showWarning({LockedCardWarning}); + else + { + if(pin1Blocked) + ui->warnings->showWarning({UnblockPin1Warning, 0, + [this]{ changePinClicked(QSmartCardData::Pin1Type, QSmartCard::UnblockWithPuk); }}); + + if(pin2Locked && pin2Blocked) + ui->warnings->showWarning({ActivatePin2WithPUKWarning, 0, + [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPuk); }}); + else if(pin2Blocked) + ui->warnings->showWarning({UnblockPin2Warning, 0, + [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::UnblockWithPuk); }}); + else if(pin2Locked) + ui->warnings->showWarning({ActivatePin2Warning, 0, + [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPin); }}); + } const qint64 DAY = 24 * 60 * 60; qint64 expiresIn = 106 * DAY; @@ -741,12 +743,12 @@ void MainWindow::updateMyEid(const QSmartCardData &data) if(expiresIn <= 0) { ui->myEid->invalidIcon(true); - ui->warnings->showWarning({WarningType::CertExpiredError}); + ui->warnings->showWarning({CertExpiredError}); } else if(expiresIn <= 105 * DAY) { ui->myEid->warningIcon(true); - ui->warnings->showWarning({WarningType::CertExpiryWarning}); + ui->warnings->showWarning({CertExpiryWarning}); } } diff --git a/client/MainWindow.h b/client/MainWindow.h index 3fc981369..393b25398 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -37,11 +37,18 @@ class MainWindow final : public QWidget Q_OBJECT public: + enum Pages : unsigned char { + SignIntro, + SignDetails, + CryptoIntro, + CryptoDetails, + MyEid + }; explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow() final; + ~MainWindow() noexcept final; void openFiles(QStringList files, bool addFile = false, bool forceCreate = false); - void selectPage(ria::qdigidoc4::Pages page); + void selectPage(Pages page); void showSettings(int page); protected: @@ -59,8 +66,7 @@ class MainWindow final : public QWidget void convertToCDoc(); ria::qdigidoc4::ContainerState currentState(); bool encrypt(); - void loadPicture(); - void navigateToPage( ria::qdigidoc4::Pages page, const QStringList &files = QStringList(), bool create = true ); + void navigateToPage(Pages page, const QStringList &files = QStringList(), bool create = true); void onCryptoAction(int action, const QString &id, const QString &phone); void onSignAction(int action, const QString &idCode, const QString &info2); void openContainer(bool signature); @@ -78,5 +84,5 @@ class MainWindow final : public QWidget std::unique_ptr cryptoDoc; std::unique_ptr digiDoc; - Ui::MainWindow *ui; + std::unique_ptr ui; }; diff --git a/client/QSmartCard.cpp b/client/QSmartCard.cpp index 3e3c7fffd..fad94a5aa 100644 --- a/client/QSmartCard.cpp +++ b/client/QSmartCard.cpp @@ -480,7 +480,10 @@ bool THALESCard::updateCounters(QSmartCardDataPrivate *d) const { d->retry[QSmartCardData::PinType(type)] = quint8(retry.data[0]); auto changed = info[0xDF2F]; - d->locked[QSmartCardData::PinType(type)] = changed && changed.data[0] == 0; + d->locked[QSmartCardData::PinType(type)] = (changed && changed.data[0] == 0); + // FIXME: remove from production + if (type == QSmartCardData::Pin1Type && qEnvironmentVariableIsSet("PIN1_LOCKED")) + d->locked[QSmartCardData::PinType(type)] = true; } else return false; diff --git a/client/common_enums.h b/client/common_enums.h index 4c48b0978..b2b5fc751 100644 --- a/client/common_enums.h +++ b/client/common_enums.h @@ -53,34 +53,4 @@ enum Actions : unsigned char { ClearCryptoWarning, }; -enum Pages : unsigned char { - SignIntro, - SignDetails, - CryptoIntro, - CryptoDetails, - MyEid -}; - -enum WarningType : unsigned char { - NoWarning = 0, - - CertExpiredError, - CertExpiryWarning, - UnblockPin1Warning, - UnblockPin2Warning, - ActivatePin2Warning, - ActivatePin1WithPUKWarning, - ActivatePin2WithPUKWarning, - - InvalidSignatureError, - InvalidTimestampError, - UnknownSignatureWarning, - UnknownTimestampWarning, - UnsupportedAsicSWarning, - UnsupportedAsicCadesWarning, - UnsupportedDDocWarning, - UnsupportedCDocWarning, - EmptyFileWarning, -}; - } diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 00d117ebe..e31cf54ec 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -328,13 +328,14 @@ void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert ui->rightPane->addWidget(new AddressItem(std::move(key), AddressItem::Icon, ui->rightPane)); } if(hasUnsupported) - emit warning({UnsupportedCDocWarning}); + emit warning({WarningText::UnsupportedCDocWarning}); ui->leftPane->setModel(container->documentModel()); updatePanes(container->state()); } void ContainerPage::transition(DigiDoc* container) { + using enum WarningText::WarningType; disconnect(ui->leftPane, &ItemList::removed, container, nullptr); connect(ui->leftPane, &ItemList::removed, container, [this, container](int index) { deleteConfirm(container, index); @@ -386,7 +387,7 @@ void ContainerPage::transition(DigiDoc* container) }); clear(ClearSignatureWarning); - std::map errors; + std::map errors; setHeader(container->fileName()); ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Container files")); diff --git a/client/widgets/SignatureItem.cpp b/client/widgets/SignatureItem.cpp index 040a87e6d..dda359d15 100644 --- a/client/widgets/SignatureItem.cpp +++ b/client/widgets/SignatureItem.cpp @@ -34,7 +34,7 @@ struct SignatureItem::Private: public Ui::SignatureItem { DigiDocSignature signature; - ria::qdigidoc4::WarningType error = ria::qdigidoc4::NoWarning; + WarningText::WarningType error = WarningText::NoWarning; QString serial; QString roleText; }; @@ -77,8 +77,9 @@ void SignatureItem::init() { const SslCertificate cert = ui->signature.cert(); + using enum WarningText::WarningType; ui->serial.clear(); - ui->error = ria::qdigidoc4::NoWarning; + ui->error = NoWarning; QString nameText; if(!cert.isNull()) { @@ -120,12 +121,12 @@ void SignatureItem::init() case DigiDocSignature::Invalid: ui->status->setLabel(QStringLiteral("error")); ui->status->setText(isSignature ? tr("Signature is not valid") : tr("Timestamp is not valid")); - ui->error = isSignature ? ria::qdigidoc4::InvalidSignatureError : ria::qdigidoc4::InvalidTimestampError; + ui->error = isSignature ? InvalidSignatureError : InvalidTimestampError; break; case DigiDocSignature::Unknown: ui->status->setLabel(QStringLiteral("error")); ui->status->setText(isSignature ? tr("Signature is unknown") : tr("Timestamp is unknown")); - ui->error = isSignature ? ria::qdigidoc4::UnknownSignatureWarning : ria::qdigidoc4::UnknownTimestampWarning; + ui->error = isSignature ? UnknownSignatureWarning : UnknownTimestampWarning; break; } @@ -183,7 +184,7 @@ bool SignatureItem::eventFilter(QObject *o, QEvent *e) return Item::eventFilter(o, e); } -ria::qdigidoc4::WarningType SignatureItem::getError() const +WarningText::WarningType SignatureItem::getError() const { return ui->error; } diff --git a/client/widgets/SignatureItem.h b/client/widgets/SignatureItem.h index cd3f88906..06516d406 100644 --- a/client/widgets/SignatureItem.h +++ b/client/widgets/SignatureItem.h @@ -21,6 +21,8 @@ #include "widgets/Item.h" +#include "widgets/WarningItem.h" + class DigiDocSignature; class SignatureItem final : public Item @@ -31,7 +33,7 @@ class SignatureItem final : public Item explicit SignatureItem(DigiDocSignature s, QWidget *parent = nullptr); ~SignatureItem() final; - ria::qdigidoc4::WarningType getError() const; + WarningText::WarningType getError() const; void initTabOrder(QWidget *item) final; bool isSelfSigned(const QString& cardCode) const; QWidget* lastTabWidget() final; diff --git a/client/widgets/VerifyCert.cpp b/client/widgets/VerifyCert.cpp index c5b35eeaa..29b95ff94 100644 --- a/client/widgets/VerifyCert.cpp +++ b/client/widgets/VerifyCert.cpp @@ -48,7 +48,7 @@ VerifyCert::VerifyCert(QWidget *parent) CertificateDetails::showCertificate(c, this, pinType == QSmartCardData::Pin1Type ? QStringLiteral("-auth") : QStringLiteral("-sign")); }); - connect(ui->checkCert, &QToolButton::clicked, this, [this]{ + connect(ui->checkCert, &QToolButton::clicked, this, [this] { auto *dlg = WarningDialog::create(this); QString readMore = tr("Read more here."); switch(c.validateOnline()) @@ -118,10 +118,10 @@ void VerifyCert::update(QSmartCardData::PinType type, const QSmartCardData &data update(); } -void VerifyCert::update(QSmartCardData::PinType type, const SslCertificate &cert) +void VerifyCert::update(QSmartCardData::PinType type, SslCertificate cert) { pinType = type; - c = cert; + c = std::move(cert); update(); } @@ -129,6 +129,7 @@ void VerifyCert::update() { if(cardData.isNull() && c.isNull()) return clear(); + bool isLockedCard = !cardData.isNull() && cardData.pinLocked(QSmartCardData::Pin1Type); bool isLockedPin = !cardData.isNull() && pinType == QSmartCardData::Pin2Type && cardData.pinLocked(pinType); bool isBlockedPin = !cardData.isNull() && cardData.retryCount(pinType) == 0; bool isBlockedPuk = !cardData.isNull() && cardData.retryCount(QSmartCardData::PukType) == 0; @@ -183,6 +184,9 @@ void VerifyCert::update() { ui->validUntil->setText(tr("Certificate has expired!")); ui->validUntil->setLabel(QStringLiteral("error")); + icon = QStringLiteral(":/images/icon_alert_large_error.svg"); + ui->info->setLabel(QStringLiteral("error")); + ui->info->setText(tr("PIN%1 can not be used because the certificate has expired.").arg(pinType)); } else if(qint64 leftDays = std::max(0, QDateTime::currentDateTime().daysTo(c.expiryDate().toLocalTime())); leftDays <= 105 && !c.isNull()) { @@ -194,18 +198,20 @@ void VerifyCert::update() ui->changePIN->setText(tr("Change PIN%1").arg(pinType)); ui->forgotPinLink->setText(tr("Change with PUK code")); - ui->changePIN->setHidden((isBlockedPin && isBlockedPuk) || isTempelType); + ui->changePIN->setHidden(isLockedCard || (isBlockedPin && isBlockedPuk) || isTempelType); if(isTempelType) { ui->info->setLabel({}); ui->info->setText(tr("PIN can be changed only using eToken utility")); } - else if(isInvalidCert) + else if(isLockedCard) { - icon = QStringLiteral(":/images/icon_alert_large_error.svg"); - ui->info->setLabel(QStringLiteral("error")); - ui->info->setText(tr("PIN%1 can not be used because the certificate has expired.").arg(pinType)); + icon = QStringLiteral(":/images/icon_alert_large_warning.svg"); + ui->info->setLabel(QStringLiteral("warning")); + ui->info->setText(pinType == QSmartCardData::Pin1Type ? + tr("The ID-card must be activated in order to authenticate") : + tr("The ID-card must be activated in order to sign")); } else if(isBlockedPin) { @@ -234,9 +240,9 @@ void VerifyCert::update() if(!icon.isEmpty()) ui->nameIcon->load(icon); - ui->links->setHidden(pinType == QSmartCardData::PukType && (isBlockedPuk || !isPUKReplacable)); // Keep visible in PUK to align fields equaly + ui->links->setHidden(pinType == QSmartCardData::PukType && ui->changePIN->isHidden()); ui->details->setHidden(pinType == QSmartCardData::PukType); - ui->forgotPinLink->setHidden(pinType == QSmartCardData::PukType || isBlockedPin || isBlockedPuk || isTempelType); + ui->forgotPinLink->setHidden(pinType == QSmartCardData::PukType || isLockedCard || isBlockedPin || isBlockedPuk || isTempelType); ui->checkCert->setHidden(pinType == QSmartCardData::PukType || isInvalidCert); } diff --git a/client/widgets/VerifyCert.h b/client/widgets/VerifyCert.h index e160bc27f..c3f2bb8b7 100644 --- a/client/widgets/VerifyCert.h +++ b/client/widgets/VerifyCert.h @@ -36,7 +36,7 @@ class VerifyCert final : public StyledWidget void clear(); void update(QSmartCardData::PinType type, const QSmartCardData &data); - void update(QSmartCardData::PinType type, const SslCertificate &cert); + void update(QSmartCardData::PinType type, SslCertificate cert); signals: void changePinClicked(QSmartCard::PinAction); diff --git a/client/widgets/VerifyCert.ui b/client/widgets/VerifyCert.ui index 5e2a67847..b01b6f5dc 100644 --- a/client/widgets/VerifyCert.ui +++ b/client/widgets/VerifyCert.ui @@ -70,6 +70,19 @@ background-color: #2B66A6; #changePIN[active="true"]:pressed { border-color: #215081; background-color: #215081; +} +#links > QToolButton { +color: #2F70B6; +border: none; +font-family: Roboto, Helvetica; +font-size: 14px; +text-decoration: underline solid; +} +#links > QToolButton:hover, #links > QToolButton:focus { +background-color: #EAF1F8; +} +#links > QToolButton:pressed { +background-color: #BFD3E8; } @@ -94,54 +107,52 @@ background-color: #215081; 0 - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 0 - 20 - - - - - - - - - 24 - 24 - - - - - 24 - 24 - - - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + @@ -265,22 +276,7 @@ background-color: #215081; 57 - - QToolButton { -color: #2F70B6; -border: none; -font-family: Roboto, Helvetica; -font-size: 14px; -text-decoration: underline solid; -} -QToolButton:hover, QToolButton:focus { -background-color: #EAF1F8; -} -QToolButton:pressed { -background-color: #BFD3E8; -} - - + 0 diff --git a/client/widgets/WarningItem.cpp b/client/widgets/WarningItem.cpp index d7b2423e8..9e9d2ce42 100644 --- a/client/widgets/WarningItem.cpp +++ b/client/widgets/WarningItem.cpp @@ -64,7 +64,7 @@ void WarningItem::changeEvent(QEvent* event) void WarningItem::mousePressEvent(QMouseEvent */*event*/) { // this warning should not be closed if there are zero-byte(empty) files in the container - if (warnText.type != EmptyFileWarning) + if (warnText.type != WarningText::EmptyFileWarning) deleteLater(); } @@ -72,6 +72,8 @@ void WarningItem::lookupWarning() { switch(warnText.type) { + using enum WarningText::WarningType; + using enum MainWindow::Pages; case CertExpiredError: setObjectName("WarningItemError"); ui->warningText->setText(tr("Certificates have expired!")); @@ -105,6 +107,13 @@ void WarningItem::lookupWarning() url = tr("https://www.id.ee/en/article/changing-id-card-pin-codes-and-puk-code/"); _page = MyEid; break; + case LockedCardWarning: + ui->warningText->setText(tr("Authentication and signing with the ID-card isn't possible yet. " + "ID-card must be activated in the Police and Border Guard Board’s self-service portal in order to use it.")); + ui->warningAction->setText(VerifyCert::tr("Activate ID-card")); + url = tr("https://www.politsei.ee/en/self-service-portal/"); + _page = MyEid; + break; // SignDetails case InvalidSignatureError: setObjectName("WarningItemError"); @@ -146,24 +155,25 @@ void WarningItem::lookupWarning() url = tr("https://www.id.ee/en/article/digidoc-container-format-life-cycle-2/"); _page = SignDetails; break; - case UnsupportedCDocWarning: - ui->warningText->setText(tr("The encrypted container contains a cryptographic algorithm or recipient type that is not supported in this DigiDoc4 application version. " - "Please make sure that you are using the latest DigiDoc4 application version.")); - url = tr("https://www.id.ee/en/article/install-id-software/"); - _page = CryptoDetails; - break; case EmptyFileWarning: ui->warningText->setText(tr("An empty file is attached to the container. " "Remove the empty file from the container to sign.")); ui->warningAction->hide(); _page = SignDetails; break; + // CryptoDetails + case UnsupportedCDocWarning: + ui->warningText->setText(tr("The encrypted container contains a cryptographic algorithm or recipient type that is not supported in this DigiDoc4 application version. " + "Please make sure that you are using the latest DigiDoc4 application version.")); + url = tr("https://www.id.ee/en/article/install-id-software/"); + _page = CryptoDetails; + break; case NoWarning: break; } } -WarningType WarningItem::warningType() const +WarningText::WarningType WarningItem::warningType() const { return warnText.type; } diff --git a/client/widgets/WarningItem.h b/client/widgets/WarningItem.h index a9d971eac..4e83b5514 100644 --- a/client/widgets/WarningItem.h +++ b/client/widgets/WarningItem.h @@ -21,10 +21,33 @@ #include "StyledWidget.h" +#include "MainWindow.h" + namespace Ui { class WarningItem; } struct WarningText { - ria::qdigidoc4::WarningType type = ria::qdigidoc4::NoWarning; + enum WarningType : unsigned char { + NoWarning = 0, + + CertExpiredError, + CertExpiryWarning, + UnblockPin1Warning, + UnblockPin2Warning, + ActivatePin2Warning, + ActivatePin1WithPUKWarning, + ActivatePin2WithPUKWarning, + LockedCardWarning, + + InvalidSignatureError, + InvalidTimestampError, + UnknownSignatureWarning, + UnknownTimestampWarning, + UnsupportedAsicSWarning, + UnsupportedAsicCadesWarning, + UnsupportedDDocWarning, + UnsupportedCDocWarning, + EmptyFileWarning, + } type = NoWarning; int counter = 0; std::function cb; }; @@ -39,7 +62,7 @@ class WarningItem final: public StyledWidget ~WarningItem() final; int page() const; - ria::qdigidoc4::WarningType warningType() const; + WarningText::WarningType warningType() const; private: Q_DISABLE_COPY(WarningItem) @@ -50,5 +73,5 @@ class WarningItem final: public StyledWidget Ui::WarningItem *ui; WarningText warnText; QString url; - ria::qdigidoc4::Pages _page = ria::qdigidoc4::MyEid; + MainWindow::Pages _page = MainWindow::MyEid; }; diff --git a/client/widgets/WarningList.cpp b/client/widgets/WarningList.cpp index e76287d9b..7ba1d706b 100644 --- a/client/widgets/WarningList.cpp +++ b/client/widgets/WarningList.cpp @@ -31,5 +31,5 @@ void WarningList::showWarning(WarningText warningText) void WarningList::updateWarnings(int page) { for(auto *warning: findChildren()) - warning->setVisible(warning->page() == page || warning->page() == ria::qdigidoc4::MyEid); + warning->setVisible(warning->page() == page || warning->page() == MainWindow::MyEid); }