Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions client/CDoc2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,18 +715,19 @@ QByteArray CDoc2::transportKey(const CKey &_key)
}
if(!cdoc20::checkConnection())
return {};
qApp->signer()->setCachePIN(true);
auto authKey = dispatchToMain(&QSigner::key, qApp->signer());
QScopedPointer<QNetworkAccessManager,QScopedPointerDeleteLater> nam(
CheckConnection::setupNAM(req, qApp->signer()->tokenauth().cert(), authKey, Settings::CDOC2_GET_CERT));
QEventLoop e;
QNetworkReply *reply = nam->get(req);
connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit);
e.exec();
if(authKey.handle())
qApp->signer()->logout();
qApp->signer()->setCachePIN(false);
if(reply->error() != QNetworkReply::NoError &&
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 201)
{
qApp->signer()->logout();
setLastError(reply->errorString());
return {};
}
Expand All @@ -752,6 +753,7 @@ QByteArray CDoc2::transportKey(const CKey &_key)
QByteArray info = KEK + cdoc20::Header::EnumNameFMKEncryptionMethod(cdoc20::Header::FMKEncryptionMethod::XOR) + key.key + key.publicKey;
return Crypto::expand(kekPm, info, KEY_LEN);
});
qApp->signer()->logout();
if(kek.isEmpty())
{
setLastError(QStringLiteral("Failed to derive key"));
Expand Down
18 changes: 0 additions & 18 deletions client/CryptoDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,24 +374,6 @@ bool CryptoDoc::decrypt()
return false;
}

if(d->cdoc->version() == 2 && !key.transaction_id.isEmpty() && !Settings::CDOC2_NOTIFICATION.isSet())
{
auto *dlg = new WarningDialog(tr("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."), Application::mainWindow());
dlg->setCancelText(WarningDialog::Cancel);
dlg->addButton(WarningDialog::OK, QMessageBox::Ok);
dlg->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->key = d->cdoc->transportKey(key);
#ifndef NDEBUG
qDebug() << "Transport key" << d->key.toHex();
Expand Down
26 changes: 21 additions & 5 deletions client/QCNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ class QCNG::Private
{
public:
TokenData token;
NCRYPT_KEY_HANDLE key {};
QCNG::PinStatus err = QCNG::PinOK;
~Private() {
if (key) NCryptFreeObject(key);
}
};

QCNG::QCNG( QObject *parent )
Expand All @@ -56,8 +60,19 @@ QCNG::~QCNG()
delete d;
}

void
QCNG::logout() {
if (d->key) {
// https://docs.microsoft.com/en-us/archive/blogs/alejacma/smart-cards-pin-gets-cached
NCryptSetProperty(d->key, NCRYPT_PIN_PROPERTY, nullptr, 0, 0);
NCryptFreeObject(d->key);
d->key = {};
}
}

QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const
{
// Do not reset pin here
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;
Expand Down Expand Up @@ -157,14 +172,15 @@ QByteArray QCNG::exec(F &&func) const
SCOPE<NCRYPT_PROV_HANDLE> prov;
if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(d->token.data(u"provider"_s).toString().utf16()), 0)))
return {};
SCOPE<NCRYPT_KEY_HANDLE> key;
if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(d->token.data(u"key"_s).toString().utf16()),
if (d->key) {
NCryptFreeObject(d->key);
d->key = {};
}
if(FAILED(NCryptOpenKey(prov, &d->key, LPWSTR(d->token.data(u"key"_s).toString().utf16()),
d->token.data(u"spec"_s).value<DWORD>(), 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(prov, d->key, result))
{
case ERROR_SUCCESS:
d->err = PinOK;
Expand Down
2 changes: 1 addition & 1 deletion client/QCNG.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class QCNG final: public QCryptoBackend
QByteArray deriveHMACExtract(const QByteArray &publicKey, const QByteArray &salt, int keySize) const final;
PinStatus lastError() const final;
PinStatus login(const TokenData &token) final;
void logout() final {}
void logout() final;
QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final;

private:
Expand Down
112 changes: 64 additions & 48 deletions client/QSigner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class QSigner::Private final
TokenData auth, sign;
QList<TokenData> cache;
QReadWriteLock lock;
bool logged_in = false;
bool cache_pin = false;

static ECDSA_SIG* ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
Expand Down Expand Up @@ -100,8 +102,6 @@ int QSigner::Private::rsa_sign(int type, const unsigned char *m, unsigned int m_
return 1;
}



using namespace digidoc;

QSigner::QSigner(QObject *parent)
Expand Down Expand Up @@ -198,32 +198,33 @@ X509Cert QSigner::cert() const

QByteArray QSigner::decrypt(std::function<QByteArray (QCryptoBackend *)> &&func)
{
if(!d->lock.tryLockForWrite(10 * 1000))
{
Q_EMIT error( tr("Signing/decrypting is already in progress another window.") );
return {};
}

if( d->auth.cert().isNull() )
{
Q_EMIT error( tr("Authentication certificate is not selected.") );
d->lock.unlock();
return {};
}

switch(auto status = QCryptoBackend::PinStatus(login(d->auth)))
{
case QCryptoBackend::PinOK: break;
case QCryptoBackend::PinCanceled: return {};
case QCryptoBackend::PinLocked:
Q_EMIT error(QCryptoBackend::errorString(status));
return {};
default:
Q_EMIT error(tr("Failed to login token") + ' ' + QCryptoBackend::errorString(status));
return {};
}
if (!d->logged_in) {
if(!d->lock.tryLockForWrite(10 * 1000))
{
Q_EMIT error( tr("Signing/decrypting is already in progress another window.") );
return {};
}
switch(auto status = QCryptoBackend::PinStatus(login(d->auth)))
{
case QCryptoBackend::PinOK: break;
case QCryptoBackend::PinCanceled: return {};
case QCryptoBackend::PinLocked:
Q_EMIT error(QCryptoBackend::errorString(status));
return {};
default:
Q_EMIT error(tr("Failed to login token") + ' ' + QCryptoBackend::errorString(status));
return {};
}
}
QByteArray result = waitFor(func, d->backend);
logout();
if (!d->cache_pin) logout();
if(d->backend->lastError() == QCryptoBackend::PinCanceled)
return {};

Expand All @@ -237,10 +238,12 @@ QSslKey QSigner::key() const
QSslKey key = d->auth.cert().publicKey();
if(!key.handle())
return {};
if(!d->lock.tryLockForWrite(10 * 1000))
return {};
if(login(d->auth) != QCryptoBackend::PinOK)
return {};
if (!d->logged_in) {
if(!d->lock.tryLockForWrite(10 * 1000))
return {};
if(login(d->auth) != QCryptoBackend::PinOK)
return {};
}
if(key.algorithm() == QSsl::Ec)
{
auto *ec = (EC_KEY*)key.handle();
Expand All @@ -260,21 +263,27 @@ quint8 QSigner::login(const TokenData &cert) const
{
switch(auto status = d->backend->login(cert))
{
case QCryptoBackend::PinOK: return status;
case QCryptoBackend::PinOK:
d->logged_in = true;
return status;
case QCryptoBackend::PinIncorrect:
(new WarningDialog(QCryptoBackend::errorString(status), Application::mainWindow()))->exec();
return login(cert);
default:
d->lock.unlock();
d->smartcard->reloadCounters(); // QSmartCard should also know that PIN is blocked.
d->logged_in = false;
return status;
}
}

void QSigner::logout() const
{
d->backend->logout();
d->lock.unlock();
if (d->logged_in) {
d->backend->logout();
d->lock.unlock();
d->logged_in = false;
}
d->smartcard->reloadCounters(); // QSmartCard should also know that PIN1 info is updated
}

Expand Down Expand Up @@ -405,28 +414,28 @@ std::vector<unsigned char> QSigner::sign(const std::string &method, const std::v
throw e; \
}

if(!d->lock.tryLockForWrite(10 * 1000))
throwException(tr("Signing/decrypting is already in progress another window."), Exception::General)

if( d->sign.cert().isNull() )
{
d->lock.unlock();
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);
}
QByteArray sig = waitFor(&QCryptoBackend::sign, d->backend,
if(!d->lock.tryLockForWrite(10 * 1000))
throwException(tr("Signing/decrypting is already in progress another window."), Exception::General)

if(d->sign.cert().isNull() )
{
d->lock.unlock();
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);
}
QByteArray sig = waitFor(&QCryptoBackend::sign, d->backend,
methodToNID(method), QByteArray::fromRawData((const char*)digest.data(), int(digest.size())));
logout();
if (!d->cache_pin) logout();
if(d->backend->lastError() == QCryptoBackend::PinCanceled)
throwException(tr("Failed to login token"), Exception::PINCanceled)

Expand All @@ -438,3 +447,10 @@ std::vector<unsigned char> QSigner::sign(const std::string &method, const std::v
QSmartCard * QSigner::smartcard() const { return d->smartcard; }
TokenData QSigner::tokenauth() const { return d->auth; }
TokenData QSigner::tokensign() const { return d->sign; }

void
QSigner::setCachePIN(bool cache_pin)
{
d->cache_pin = cache_pin;
}

1 change: 1 addition & 0 deletions client/QSigner.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class QSigner final: public QThread, public digidoc::Signer
QSmartCard * smartcard() const;
TokenData tokenauth() const;
TokenData tokensign() const;
void setCachePIN(bool cache_pin);

Q_SIGNALS:
void cacheChanged();
Expand Down
Loading