diff --git a/CMakeLists.txt b/CMakeLists.txt index 869fd88a0..50c446bd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ include_directories( ${LIBDIGIDOCPP_INCLUDE_DIR} ) set_env( TSL_URL "https://ec.europa.eu/tools/lotl/eu-lotl.xml" CACHE STRING "TSL trust list primary URL" ) set_env( TSL_INCLUDE "EE" CACHE STRING "TSL list include in binary" ) set_env( MOBILEID_URL "https://tsp.demo.sk.ee/mid-api" CACHE STRING "URL for Mobile-ID" ) +set_env( SMARTID_URL "https://sid.demo.sk.ee/smart-id-rp/v1" CACHE STRING "URL for Smart-ID" ) if(APPLE) set(BUNDLE_NAME qdigidoc4) diff --git a/client/Application.cpp b/client/Application.cpp index ee3604349..5012f3db6 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -547,6 +547,9 @@ void Application::diagnostics(QTextStream &s) << "
SIVA_URL: " << confValue(SiVaUrl).toString() #ifdef MOBILEID_URL << "
MOBILEID_URL: " << MOBILEID_URL +#endif +#ifdef SMARTID_URL + << "
SMARTID_URL: " << SMARTID_URL #endif << "

" << tr("TSL signing certs") << ":"; for(const QSslCertificate &cert: confValue(TSLCerts).value>()) @@ -1020,6 +1023,9 @@ void DdCliApplication::diagnostics(QTextStream &s) const #ifdef MOBILEID_URL s << "
MOBILEID_URL: " << MOBILEID_URL; +#endif +#ifdef SMARTID_URL + s << "
SMARTID_URL: " << SMARTID_URL; #endif s << "
TSL_URL: " << Application::confValue(Application::TSLUrl).toString(); s << "

" << "TSL signing certs:"; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a43bff83c..106a7e09c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -28,6 +28,7 @@ qt5_wrap_ui( SOURCES dialogs/RoleAddressDialog.ui dialogs/SettingsDialog.ui dialogs/SignatureDialog.ui + dialogs/SmartIDDialog.ui dialogs/WaitDialog.ui dialogs/WarningDialog.ui widgets/Accordion.ui @@ -84,6 +85,8 @@ add_executable( ${PROGNAME} WIN32 MACOSX_BUNDLE dialogs/PinUnblock.cpp dialogs/RoleAddressDialog.cpp dialogs/SettingsDialog.cpp + dialogs/SmartIDDialog.cpp + dialogs/SmartIDProgress.cpp dialogs/SignatureDialog.cpp dialogs/WaitDialog.cpp dialogs/WarningDialog.cpp @@ -123,7 +126,7 @@ target_link_libraries( ${PROGNAME} ${LDAP_LIBRARIES} ) -target_compile_definitions(${PROGNAME} PRIVATE MOBILEID_URL="${MOBILEID_URL}") +target_compile_definitions(${PROGNAME} PRIVATE MOBILEID_URL="${MOBILEID_URL}" SMARTID_URL="${SMARTID_URL}" ) if( APPLE ) target_sources( ${PROGNAME} PRIVATE Application_mac.mm dialogs/CertificateDetails_mac.mm ) diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 62a3ae995..b5b81615e 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -40,6 +40,7 @@ #include "dialogs/MobileProgress.h" #include "dialogs/RoleAddressDialog.h" #include "dialogs/SettingsDialog.h" +#include "dialogs/SmartIDProgress.h" #include "dialogs/WaitDialog.h" #include "dialogs/WarningDialog.h" #include "widgets/WarningList.h" @@ -499,6 +500,14 @@ void MainWindow::onSignAction(int action, const QString &info1, const QString &i return digiDoc->sign(city, state, zip, country, role, &m); }); break; + case SignatureSmartID: + sign([this, info1, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { + SmartIDProgress s(this); + if(!s.init(info1, info2)) + return false; + return digiDoc->sign(city, state, zip, country, role, &s); + }); + break; case ClearSignatureWarning: ui->signature->warningIcon(false); warnings->closeWarnings(SignDetails); diff --git a/client/common_enums.h b/client/common_enums.h index 45dc2e36e..e54e46b74 100644 --- a/client/common_enums.h +++ b/client/common_enums.h @@ -63,6 +63,7 @@ enum Actions { SignatureAdd, SignatureMobile, + SignatureSmartID, SignatureToken, SignatureRemove, SignatureWarning, diff --git a/client/dialogs/MobileProgress.cpp b/client/dialogs/MobileProgress.cpp index c19f9c40c..8b72555e3 100644 --- a/client/dialogs/MobileProgress.cpp +++ b/client/dialogs/MobileProgress.cpp @@ -161,7 +161,7 @@ MobileProgress::MobileProgress(QWidget *parent) returnError(tr("Connecting to SK server failed! Please check your internet connection.")); return; case QNetworkReply::ConnectionRefusedError: - returnError(tr("Mobile-ID service has encountered technical errors. Please try again later.")); + returnError(tr("%1 service has encountered technical errors. Please try again later.").arg(tr("Mobile-ID"))); return; case QNetworkReply::SslHandshakeFailedError: d->l.exit(Private::GeneralError); diff --git a/client/dialogs/SmartIDDialog.cpp b/client/dialogs/SmartIDDialog.cpp new file mode 100644 index 000000000..e3725e3ef --- /dev/null +++ b/client/dialogs/SmartIDDialog.cpp @@ -0,0 +1,47 @@ +#include "SmartIDDialog.h" +#include "ui_SmartIDDialog.h" + +#include "Settings.h" +#include "Styles.h" +#include "effects/Overlay.h" + +SmartIDDialog::SmartIDDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::SmartIDDialog) +{ + Overlay *overlay = new Overlay(parent->topLevelWidget()); + overlay->show(); + connect(this, &SmartIDDialog::destroyed, overlay, &Overlay::deleteLater); + + ui->setupUi(this); + setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint ); + setWindowModality( Qt::ApplicationModal ); + setFixedSize( size() ); + QFont condensed12 = Styles::font(Styles::Condensed, 12); + QFont condensed14 = Styles::font(Styles::Condensed, 14); + ui->labelPhone->setFont(condensed12); + ui->idCode->setFont(condensed12); + ui->sign->setFont(condensed14); + ui->cancel->setFont(condensed14); + ui->idCode->setText(Settings(qApp->applicationName()).value(QStringLiteral("SmartID")).toString()); + connect(ui->sign, &QPushButton::clicked, this, &QDialog::accept); + connect(ui->cancel, &QPushButton::clicked, this, &QDialog::reject); + connect(ui->idCode, &QLineEdit::textEdited, this, [](const QString &text){ + Settings(qApp->applicationName()).setValueEx(QStringLiteral("SmartID"), text, QString()); + }); +} + +SmartIDDialog::~SmartIDDialog() +{ + delete ui; +} + +QString SmartIDDialog::country() const +{ + return QStringLiteral("EE"); +} + +QString SmartIDDialog::idCode() const +{ + return ui->idCode->text(); +} diff --git a/client/dialogs/SmartIDDialog.h b/client/dialogs/SmartIDDialog.h new file mode 100644 index 000000000..97683ada2 --- /dev/null +++ b/client/dialogs/SmartIDDialog.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Ui { class SmartIDDialog; } + +class SmartIDDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SmartIDDialog(QWidget *parent = nullptr); + ~SmartIDDialog() final; + + QString country() const; + QString idCode() const; + +private: + Ui::SmartIDDialog *ui; +}; diff --git a/client/dialogs/SmartIDDialog.ui b/client/dialogs/SmartIDDialog.ui new file mode 100644 index 000000000..d0acd8032 --- /dev/null +++ b/client/dialogs/SmartIDDialog.ui @@ -0,0 +1,217 @@ + + + SmartIDDialog + + + + 0 + 0 + 380 + 282 + + + + Smart-ID + + + QDialog { +border-radius: 2px; +background-color: #FFFFFF; +} +QLineEdit, QComboBox { +padding: 0px 10px; +border: 1px solid #DEE4E9; +border-radius: 2px; +} + + + + 0 + + + 50 + + + 30 + + + 50 + + + 30 + + + + + Qt::Vertical + + + + 20 + 67 + + + + + + + + + Roboto Condensed + 12 + + + + color: #353739; + + + Personal code + + + + + + + + + + 0 + 34 + + + + + EE + + + + + LT + + + + + LV + + + + + + + + + 0 + 34 + + + + 47101010033 + + + + + + + + + Qt::Vertical + + + + 20 + 66 + + + + + + + + 40 + + + + + true + + + + 120 + 30 + + + + + 14 + + + + PointingHandCursor + + + QPushButton { + border-radius: 2px; + border: none; + color: #ffffff; + background-color: #981E32; +} +QPushButton:pressed { + background-color: #F24A66; +} +QPushButton:hover:!pressed { + background-color: #CD2541; +} + + + CANCEL + + + false + + + + + + + + 120 + 30 + + + + + 14 + + + + QPushButton { + border-radius: 2px; + border: none; + color: #ffffff; + background-color: #006EB5; +} +QPushButton:pressed { + background-color: #41B6E6; +} +QPushButton:hover:!pressed { + background-color: #008DCF; +} +QPushButton:disabled { + background-color: #BEDBED; +} + + + SIGN + + + true + + + + + + + + + + diff --git a/client/dialogs/SmartIDProgress.cpp b/client/dialogs/SmartIDProgress.cpp new file mode 100644 index 000000000..1a9427627 --- /dev/null +++ b/client/dialogs/SmartIDProgress.cpp @@ -0,0 +1,345 @@ +/* + * QDigiDocClient + * + * 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 + * + */ + +#include "SmartIDProgress.h" +#include "ui_MobileProgress.h" + +#include "Styles.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_WIN +#include +#include +#endif + +#include +#include + +#include + +Q_LOGGING_CATEGORY(SIDLog,"RIA.SmartID") + +class SmartIDProgress::Private: public QDialog, public Ui::MobileProgress +{ + Q_OBJECT +public: + QString URL() { return UUID.isNull() ? PROXYURL : SKURL; } + enum { + GeneralError = -1, + AccountNotFound = -2, + SessionNotNound = -3, + }; + using QDialog::QDialog; + QTimeLine *statusTimer = nullptr; + QNetworkAccessManager *manager = nullptr; + QNetworkRequest req; + QString documentNumber, sessionID, lastError; + digidoc::X509Cert cert; + std::vector signature; + QEventLoop l; +#ifdef CONFIG_URL + QString PROXYURL = Configuration::instance().object().value(QStringLiteral("SID-PROXY-URL")).toString(QStringLiteral(SMARTID_URL)); + QString SKURL = Configuration::instance().object().value(QStringLiteral("SID-SK-URL")).toString(QStringLiteral(SMARTID_URL)); +#else + QString PROXYURL = Settings(qApp->applicationName()).value(QStringLiteral("SID-PROXY-URL"), QStringLiteral(SMARTID_URL)).toString(); + QString SKURL = Settings(qApp->applicationName()).value(QStringLiteral("SID-SK-URL"), QStringLiteral(SMARTID_URL)).toString(); +#endif + QString NAME = Settings(qApp->applicationName()).value(QStringLiteral("SIDNAME"), QStringLiteral("DigiDoc4")).toString(); + QUuid UUID = Settings(qApp->applicationName()).value(QStringLiteral("SIDUUID")).toUuid(); +#ifdef Q_OS_WIN + QWinTaskbarButton *taskbar = nullptr; +#endif +}; + + + +SmartIDProgress::SmartIDProgress(QWidget *parent) + : d(new Private(parent)) +{ + d->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint); + d->setWindowModality(Qt::ApplicationModal); + d->setupUi(d); + d->code->setBuddy(d->signProgressBar); + d->code->setFont(Styles::font(Styles::Regular, 20, QFont::DemiBold)); + d->labelError->setFont(Styles::font(Styles::Regular, 14)); + d->signProgressBar->setFont(d->labelError->font()); + d->cancel->setFont(Styles::font(Styles::Condensed, 14)); + QObject::connect(d->cancel, &QPushButton::clicked, d, &QDialog::reject); + QObject::connect(d->cancel, &QPushButton::clicked, [=] { d->l.exit(QDialog::Rejected); }); + + d->statusTimer = new QTimeLine(d->signProgressBar->maximum() * 1000, d); + d->statusTimer->setCurveShape(QTimeLine::LinearCurve); + d->statusTimer->setFrameRange(d->signProgressBar->minimum(), d->signProgressBar->maximum()); + QObject::connect(d->statusTimer, &QTimeLine::frameChanged, d->signProgressBar, &QProgressBar::setValue); + QObject::connect(d->statusTimer, &QTimeLine::finished, d, &QDialog::reject); +#ifdef Q_OS_WIN + d->taskbar = new QWinTaskbarButton(d); + d->taskbar->setWindow(parent->windowHandle()); + d->taskbar->progress()->setRange(d->signProgressBar->minimum(), d->signProgressBar->maximum()); + QObject::connect(d->statusTimer, &QTimeLine::frameChanged, d->taskbar->progress(), &QWinTaskbarProgress::setValue); +#endif + + QSslConfiguration ssl = QSslConfiguration::defaultConfiguration(); +#ifdef CONFIG_URL + ssl.setCaCertificates({}); + QList trusted; + for(const QJsonValue &cert: Configuration::instance().object().value(QStringLiteral("CERT-BUNDLE")).toArray()) + trusted << QSslCertificate(QByteArray::fromBase64(cert.toString().toLatin1()), QSsl::Der); +#endif + d->req.setSslConfiguration(ssl); + d->req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + d->req.setRawHeader("User-Agent", QStringLiteral("%1/%2 (%3)") + .arg(qApp->applicationName(), qApp->applicationVersion(), Common::applicationOs()).toUtf8()); + d->manager = new QNetworkAccessManager(d); + QObject::connect(d->manager, &QNetworkAccessManager::sslErrors, [=](QNetworkReply *reply, const QList &err) { + QStringList msg; + QList ignore; + for(const QSslError &e: err) + { + switch(e.error()) + { + case QSslError::UnableToGetLocalIssuerCertificate: + case QSslError::CertificateUntrusted: + case QSslError::SelfSignedCertificateInChain: + if(trusted.contains(reply->sslConfiguration().peerCertificate())) { + ignore << e; + break; + } + default: + qCWarning(SIDLog) << "SSL Error:" << e.error() << e.certificate().subjectInfo(QSslCertificate::CommonName); + msg << e.errorString(); + break; + } + } + reply->ignoreSslErrors(ignore); + if(!msg.empty()) + { + d->labelError->setText(tr("SSL handshake failed. Check the proxy settings of your computer or software upgrades.")); + d->code->hide(); + } + }); + QNetworkAccessManager::connect(d->manager, &QNetworkAccessManager::finished, d, [&](QNetworkReply *reply){ + QScopedPointer scope(reply); + auto returnError = [=](const QString &err) { + qCWarning(SIDLog) << (d->lastError = err); + d->l.exit(Private::GeneralError); + }; + + switch(reply->error()) + { + case QNetworkReply::NoError: + break; + case QNetworkReply::ContentNotFoundError: + d->l.exit(d->sessionID.isEmpty() ? Private::AccountNotFound : Private::SessionNotNound); + return; + case QNetworkReply::HostNotFoundError: + returnError(tr("Connecting to SK server failed! Please check your internet connection.")); + return; + case QNetworkReply::ConnectionRefusedError: + returnError(tr("%1 service has encountered technical errors. Please try again later.").arg(tr("Smart-ID"))); + return; + case QNetworkReply::SslHandshakeFailedError: + d->l.exit(Private::GeneralError); + return; + case QNetworkReply::ProtocolInvalidOperationError: + qCWarning(SIDLog) << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + returnError(reply->readAll()); + return; + default: + qCWarning(SIDLog) << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << "Error :" << reply->error(); + if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 429) + returnError(tr("The limit for digital signatures per month has been reached for this IP address. ")); + else + returnError(tr("Failed to send request ") + reply->errorString()); + return; + } + static const QStringList contentType{"application/json", "application/json;charset=UTF-8"}; + if(!contentType.contains(reply->header(QNetworkRequest::ContentTypeHeader).toString())) + { + returnError(tr("Invalid content type header ") + reply->header(QNetworkRequest::ContentTypeHeader).toString()); + return; + } + + QByteArray data = reply->readAll(); + reply->deleteLater(); + qCDebug(SIDLog).noquote() << data; + QJsonObject result = QJsonDocument::fromJson(data, nullptr).object(); + if(result.isEmpty()) + { + returnError(tr("Failed to parse JSON content")); + return; + } + if(d->sessionID.isEmpty()) + d->sessionID = result.value(QStringLiteral("sessionID")).toString(); + else if(result.value(QStringLiteral("state")).toString() != QStringLiteral("RUNNING")) + { + QString endResult = result.value(QStringLiteral("result")).toObject().value(QStringLiteral("endResult")).toString(); + if(endResult == QStringLiteral("USER_REFUSED")) + d->l.exit(QDialog::Rejected); + else if(endResult != QStringLiteral("OK")) + returnError(tr("Service result: ") + endResult); + else if(d->documentNumber.isEmpty()) + d->documentNumber = result.value(QStringLiteral("result")).toObject().value(QStringLiteral("documentNumber")).toString(); + if(result.contains(QStringLiteral("signature"))) + { + QByteArray b64 = QByteArray::fromBase64( + result.value(QStringLiteral("signature")).toObject().value(QStringLiteral("value")).toString().toUtf8()); + d->signature.assign(b64.cbegin(), b64.cend()); + d->l.exit(QDialog::Accepted); + } + else if(result.contains(QStringLiteral("cert"))) + { + try { + QByteArray b64 = QByteArray::fromBase64( + result.value(QStringLiteral("cert")).toObject().value(QStringLiteral("value")).toString().toUtf8()); + d->cert = digidoc::X509Cert((const unsigned char*)b64.constData(), size_t(b64.size()), digidoc::X509Cert::Der); + d->l.exit(QDialog::Accepted); + } catch(const digidoc::Exception &e) { + returnError(tr("Failed to parse certificate: ") + QString::fromStdString(e.msg())); + } + } + return; + } + d->req.setUrl(QUrl(QStringLiteral("%1/session/%2?timeoutMs=10000").arg(d->URL(), d->sessionID))); + qCDebug(SIDLog).noquote() << d->req.url(); + d->manager->get(d->req); + }); +} + +SmartIDProgress::~SmartIDProgress() +{ + d->deleteLater(); +} + +digidoc::X509Cert SmartIDProgress::cert() const +{ + return d->cert; +} + +void SmartIDProgress::endProgress(const QString &msg) +{ + d->labelError->setText(msg); + d->signProgressBar->hide(); + stop(); +} + +bool SmartIDProgress::init(const QString &country, const QString &idCode) +{ + d->sessionID.clear(); + d->lastError.clear(); + QByteArray data = QJsonDocument(QJsonObject::fromVariantHash(QVariantHash{ + {"relyingPartyUUID", d->UUID.toString().remove('{').remove('}')}, // FIXME Qt 5.11: toString(QUuid::WithoutBraces)}, + {"relyingPartyName", d->NAME}, + {"certificateLevel", "QUALIFIED"} + })).toJson(); + d->req.setUrl(QUrl(QStringLiteral("%1/certificatechoice/pno/%2/%3").arg(d->URL(), country, idCode))); + qCDebug(SIDLog).noquote() << d->req.url() << data; + d->manager->post(d->req, data); + switch(d->l.exec()) + { + case QDialog::Accepted: return true; + case QDialog::Rejected: return false; + case Private::AccountNotFound: + endProgress(tr("Account not found")); + return false; + default: + if(d->lastError.isEmpty()) + endProgress(tr("Failed to sign container")); + else + endProgress(d->lastError); + return false; + } +} + +std::vector SmartIDProgress::sign(const std::string &method, const std::vector &digest) const +{ + d->statusTimer->stop(); + d->sessionID.clear(); + d->lastError.clear(); + + QString digestMethod; + if(method == "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256") + digestMethod = QStringLiteral("SHA256"); + else if(method == "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384") + digestMethod = QStringLiteral("SHA384"); + else if(method == "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512") + digestMethod = QStringLiteral("SHA512"); + else + throw digidoc::Exception(__FILE__, __LINE__, "Unsupported digest method"); + + QByteArray codeDiest = QCryptographicHash::hash(QByteArray::fromRawData((const char*)digest.data(), int(digest.size())), QCryptographicHash::Sha256); + int code = codeDiest.right(2).toHex().toUInt(nullptr, 16) % 10000; + d->code->setText(tr("Make sure control code matches with one in phone screen\n" + "and enter Mobile-ID PIN2-code.\nControl code: %1") + .arg(code, 4, 10, QChar('0'))); + + QByteArray data = QJsonDocument(QJsonObject::fromVariantHash({ + {"relyingPartyUUID", d->UUID.toString().remove('{').remove('}')}, // FIXME Qt 5.11: toString(QUuid::WithoutBraces)}, + {"relyingPartyName", d->NAME}, + {"certificateLevel", "QUALIFIED"}, + {"hash", QByteArray::fromRawData((const char*)digest.data(), int(digest.size())).toBase64()}, + {"hashType", digestMethod}, + {"requestProperties", QJsonObject::fromVariantHash({{"vcChoice", true}})}, + {"displayText", "Sign document"} + })).toJson(); + d->req.setUrl(QUrl(QStringLiteral("%1/signature/document/%2").arg(d->URL(), d->documentNumber))); + qCDebug(SIDLog).noquote() << d->req.url() << data; + d->manager->post(d->req, data); + d->statusTimer->start(); + d->adjustSize(); + d->show(); + switch(d->l.exec()) + { + case QDialog::Accepted: return d->signature; + case QDialog::Rejected: + { + digidoc::Exception e(__FILE__, __LINE__, "Signing canceled"); + e.setCode(digidoc::Exception::PINCanceled); + throw e; + } + default: + if(d->lastError.isEmpty()) + throw digidoc::Exception(__FILE__, __LINE__, "Failed to sign container"); + throw digidoc::Exception(__FILE__, __LINE__, d->lastError.toUtf8().constData()); + } +} + +void SmartIDProgress::stop() +{ + d->statusTimer->stop(); +#ifdef Q_OS_WIN + d->taskbar->progress()->stop(); + d->taskbar->progress()->hide(); +#endif +} + +#include "SmartIDProgress.moc" diff --git a/client/dialogs/SmartIDProgress.h b/client/dialogs/SmartIDProgress.h new file mode 100644 index 000000000..e1edf6846 --- /dev/null +++ b/client/dialogs/SmartIDProgress.h @@ -0,0 +1,44 @@ +/* + * QDigiDocClient + * + * 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 + +#include + +class QWidget; + +class SmartIDProgress: public digidoc::Signer +{ + Q_DECLARE_TR_FUNCTIONS(MobileProgress) +public: + SmartIDProgress(QWidget *parent = nullptr); + ~SmartIDProgress() final; + digidoc::X509Cert cert() const final; + bool init(const QString &country, const QString &idCode); + std::vector sign(const std::string &method, const std::vector &digest) const final; + +private: + void endProgress(const QString &msg); + void stop(); + + class Private; + Private *d; +}; diff --git a/client/translations/en.ts b/client/translations/en.ts index cc1b04c9c..df37d8561 100755 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -1207,6 +1207,11 @@ LDAP server is unavailable. SignatureMobile SIGN WITH MOBIIL-ID + + + SignatureSmartID + SIGN WITH +SMARTID SignatureAdd @@ -1655,8 +1660,8 @@ Control code: %1 Cancel - Mobile-ID service has encountered technical errors. Please try again later. - Mobile-ID service has encountered technical errors. Please try again later. + %1 service has encountered technical errors. Please try again later. + %1 service has encountered technical errors. Please try again later. Failed to send request @@ -1686,6 +1691,18 @@ Control code: %1 The limit for digital signatures per month has been reached for this IP address. <a href="https://www.id.ee/index.php?id=39023"Additional information</a> The limit for digital signatures per month has been reached for this IP address. <a href="https://www.id.ee/index.php?id=39023"Additional information</a> + + Mobile-ID + Mobile-ID + + + Smart-ID + Smart-ID + + + Service result: + + NoCardInfo @@ -2885,6 +2902,21 @@ Additional licenses and components To view signature details press enter or space + + SmartIDDialog + + SIGN + SIGN + + + CANCEL + CANCEL + + + Personal code + PERSONAL CODE + + SslCertificate diff --git a/client/translations/et.ts b/client/translations/et.ts index 33a239c9c..1b108d171 100755 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -1207,6 +1207,11 @@ LDAP serveriga ei saa ühendust. SignatureMobile ALLKIRJASTA MOBIIL-ID’GA + + + SignatureSmartID + ALLKIRJASTA +SMARTID’GA SignatureAdd @@ -1655,8 +1660,8 @@ Kontrollkood: %1 Katkesta - Mobile-ID service has encountered technical errors. Please try again later. - Mobiil-ID teenuses esinevad tehnilised tõrked. Palun proovige mõne aja pärast uuesti. + %1 service has encountered technical errors. Please try again later. + %1 teenuses esinevad tehnilised tõrked. Palun proovige mõne aja pärast uuesti. Failed to send request @@ -1686,6 +1691,18 @@ Kontrollkood: %1 The limit for digital signatures per month has been reached for this IP address. <a href="https://www.id.ee/index.php?id=39023"Additional information</a> Sinu IP-aadressi tasuta allkirjade kuulimiit on ületatud. <a href="https://id.ee/index.php?id=39021">Loe täpsemalt siit</a> + + Mobile-ID + Mobiil-ID + + + Smart-ID + Smart-ID + + + Service result: + + NoCardInfo @@ -2885,6 +2902,21 @@ Täiendavad litsentsid ja komponendid Allkirja detailide vaatamiseks vajuta space või enter + + SmartIDDialog + + SIGN + ALLKIRJASTA + + + CANCEL + KATKESTA + + + Personal code + ISIKUKOOD + + SslCertificate diff --git a/client/translations/ru.ts b/client/translations/ru.ts index f20febb39..8fc7877d3 100755 --- a/client/translations/ru.ts +++ b/client/translations/ru.ts @@ -1209,6 +1209,11 @@ LDAP сервер недоступен. SignatureMobile ПОДПИСАТЬ С MOBIIL-ID + + + SignatureSmartID + ПОДПИСАТЬ С +SMARTID SignatureAdd @@ -1658,8 +1663,8 @@ Kонтрольный код: %1 Отмена - Mobile-ID service has encountered technical errors. Please try again later. - Технический сбой услуги Mobiil-ID. Пожалуйста, попробуйте позже. + %1 service has encountered technical errors. Please try again later. + Технический сбой услуги %1. Пожалуйста, попробуйте позже. Failed to send request @@ -1689,6 +1694,18 @@ Kонтрольный код: %1 The limit for digital signatures per month has been reached for this IP address. <a href="https://www.id.ee/index.php?id=39023"Additional information</a> Предел для цифровых подписей в месяц был достигнут для этого IP-адреса. <a href="https://www.id.ee/index.php?id=39025">Дополнительная информация</a> + + Mobile-ID + Mobiil-ID + + + Smart-ID + Smart-ID + + + Service result: + + NoCardInfo @@ -2888,6 +2905,21 @@ Additional licenses and components Для просмотра деталей подписи нажмите пробел или enter + + SmartIDDialog + + SIGN + ПОДПИСАТЬ + + + CANCEL + ОТМЕНА + + + Personal code + ЛИЧНЫЙ КОД + + SslCertificate diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 9f63835ff..544636d36 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -26,6 +26,7 @@ #include "crypto/CryptoDoc.h" #include "dialogs/AddRecipients.h" #include "dialogs/MobileDialog.h" +#include "dialogs/SmartIDDialog.h" #include "dialogs/WarningDialog.h" #include "widgets/AddressItem.h" #include "widgets/SignatureItem.h" @@ -126,6 +127,7 @@ bool ContainerPage::checkAction(int code, const QString& selectedCard, const QSt case SignatureAdd: case SignatureToken: case SignatureMobile: + case SignatureSmartID: if(ui->rightPane->hasItem( [selectedCard, selectedMobile, code](Item* const item) -> bool { @@ -211,6 +213,13 @@ void ContainerPage::forward(int code) window()->setWindowTitle(tr("DigiDoc4 client")); emit action(code); break; + case SignatureSmartID: + { + SmartIDDialog dlg(qApp->activeWindow()); + if(dlg.exec() == QDialog::Accepted) + emit action(SignatureSmartID, dlg.country(), dlg.idCode()); + break; + } default: if(checkAction(code, cardInReader, mobileCode)) emit action(code); @@ -281,11 +290,11 @@ void ContainerPage::showMainAction(const QList &actions) void ContainerPage::showSigningButton() { if(cardInReader.isNull()) - showMainAction({ SignatureMobile }); + showMainAction({ SignatureMobile, SignatureSmartID }); else if(seal) - showMainAction({ SignatureToken, SignatureMobile }); + showMainAction({ SignatureToken, SignatureMobile, SignatureSmartID }); else - showMainAction({ SignatureAdd, SignatureMobile }); + showMainAction({ SignatureAdd, SignatureMobile, SignatureSmartID }); } void ContainerPage::transition(CryptoDoc* container, bool canDecrypt) diff --git a/client/widgets/MainAction.cpp b/client/widgets/MainAction.cpp index c82727234..15249b2cc 100644 --- a/client/widgets/MainAction.cpp +++ b/client/widgets/MainAction.cpp @@ -90,6 +90,7 @@ QString MainAction::label(Actions action) const switch(action) { case SignatureMobile: return tr("SignatureMobile"); + case SignatureSmartID: return tr("SignatureSmartID"); case SignatureToken: return tr("SignatureToken"); case EncryptContainer: return tr("EncryptContainer"); case DecryptContainer: return tr("DecryptContainer");