Skip to content
Open
5 changes: 5 additions & 0 deletions client/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down Expand Up @@ -429,6 +433,7 @@ Application::Application( int &argc, char **argv )
// Clear obsolete registriy settings
#ifndef Q_OS_DARWIN
Settings::DEFAULT_DIR.clear();
Settings::CDOC2_NOTIFICATION.clear();
#endif

// Actions
Expand Down
97 changes: 59 additions & 38 deletions client/CDocSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ CDocSupport::getCDocFileList(const QString &filename)
}

static libcdoc::result_t
getDecryptStatus(const std::vector<uint8_t>& 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:
Expand All @@ -109,44 +109,62 @@ getDecryptStatus(const std::vector<uint8_t>& result, QCryptoBackend::PinStatus p
}
}

static libcdoc::result_t
getDecryptResultStatus(const std::vector<uint8_t> &result, std::unique_ptr<QCryptoBackend> backend)
{
if (!result.empty())
return libcdoc::OK;
libcdoc::result_t mapped = getDecryptStatus(backend->status);
return mapped == libcdoc::OK ? DDCryptoBackend::BACKEND_ERROR : mapped;
}

libcdoc::result_t
DDCryptoBackend::decryptRSA(std::vector<uint8_t>& result, const std::vector<uint8_t> &data, bool oaep, unsigned int idx)
DDCryptoBackend::decryptRSA(std::vector<uint8_t>& dst, const std::vector<uint8_t> &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);
if (!backend) {
auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth());
if (!val)
return getDecryptStatus(val.error());
backend.reset(val.value());
}
QByteArray decryptedKey = backend->decrypt(toByteArray(data), oaep);
dst.assign(decryptedKey.cbegin(), decryptedKey.cend());
return getDecryptResultStatus(dst, std::move(backend));
}

libcdoc::result_t
DDCryptoBackend::deriveConcatKDF(std::vector<uint8_t>& dst, const std::vector<uint8_t> &publicKey, const std::string &digest,
const std::vector<uint8_t> &algorithmID, const std::vector<uint8_t> &partyUInfo, const std::vector<uint8_t> &partyVInfo, unsigned int idx)
{
QCryptoBackend::PinStatus pin_status;
QByteArray decryptedKey = qApp->signer()->decrypt([&publicKey, &digest, &algorithmID, &partyUInfo, &partyVInfo](QCryptoBackend *backend) {
static const QHash<std::string_view, QCryptographicHash::Algorithm> 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<std::string_view, QCryptographicHash::Algorithm> 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}
};
if (!backend) {
auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth());
if (!val)
return getDecryptStatus(val.error());
backend.reset(val.value());
}
QByteArray decryptedKey = backend->deriveConcatKDF(toByteArray(publicKey), SHA_MTH.value(digest),
toByteArray(algorithmID), toByteArray(partyUInfo), toByteArray(partyVInfo));
dst.assign(decryptedKey.cbegin(), decryptedKey.cend());
return getDecryptStatus(dst, pin_status);
return getDecryptResultStatus(dst, std::move(backend));
}

libcdoc::result_t
DDCryptoBackend::deriveHMACExtract(std::vector<uint8_t>& dst, const std::vector<uint8_t> &key_material, const std::vector<uint8_t> &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<uint8_t>(qkekpm.cbegin(), qkekpm.cend());
return getDecryptStatus(dst, pin_status);
if (!backend) {
auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth());
if (!val)
return getDecryptStatus(val.error());
backend.reset(val.value());
}
QByteArray decryptedKey = backend->deriveHMACExtract(toByteArray(key_material), toByteArray(salt), ECC_KEY_LEN);
dst.assign(decryptedKey.cbegin(), decryptedKey.cend());
return getDecryptResultStatus(dst, std::move(backend));
}

libcdoc::result_t
Expand Down Expand Up @@ -281,32 +299,32 @@ libcdoc::result_t DDNetworkBackend::sendKey(
};

libcdoc::result_t
DDNetworkBackend::fetchKey(std::vector<uint8_t> &result,
const std::string &url,
const std::string &transaction_id) {
DDNetworkBackend::fetchKey(std::vector<uint8_t> &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;
}
QCryptoBackend::PinStatus pin_status;
auto authKey = dispatchToMain([&] {
return qApp->signer()->key(pin_status);
});

TokenData auth = qApp->signer()->tokenauth();
auto val = QCryptoBackend::getBackend(qApp->signer()->tokenauth());
if (!val.value())
return getDecryptStatus(val.error());
std::unique_ptr<QCryptoBackend> backend(val.value());

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<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();
}

if(reply->error() != QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 201) {
last_error = reply->errorString().toStdString();
Expand All @@ -315,6 +333,9 @@ DDNetworkBackend::fetchKey(std::vector<uint8_t> &result,
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(std::move(backend));

return libcdoc::OK;
}

Expand Down
10 changes: 9 additions & 1 deletion client/CDocSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#pragma once

#include "QCryptoBackend.h"

#include <QtCore/QObject>
#include <QtCore/QIODevice>
#include <QtCore/QFile>
Expand Down Expand Up @@ -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<QCryptoBackend> backend;
std::vector<uint8_t> secret;

explicit DDCryptoBackend() = default;

void setBackend(std::unique_ptr<QCryptoBackend> &&backend) {
this->backend = std::move(backend);
}
};

//
Expand Down Expand Up @@ -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;
};

Expand Down
29 changes: 4 additions & 25 deletions client/CryptoDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ struct CryptoDoc::Private
std::vector<IOEntry> files;
std::vector<CKey> keys;

explicit Private() : network(crypto) {}
bool isEncryptedWarning(const QString &title) const;

bool isEncrypted() const {
Expand Down Expand Up @@ -340,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;
Expand Down Expand Up @@ -394,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.");
Expand Down
58 changes: 26 additions & 32 deletions client/QCNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,30 @@ struct SCOPE
constexpr T* operator&() noexcept { return &d; }
};

class QCNG::Private
struct QCNG::Private
{
public:
TokenData token;
QCNG::PinStatus err = QCNG::PinOK;
SCOPE<NCRYPT_PROV_HANDLE> prov;
SCOPE<NCRYPT_KEY_HANDLE> key;
bool pss;
};

QCNG::QCNG( QObject *parent )
: QCryptoBackend(parent)
, d(new Private)
{}
QCNG::QCNG() noexcept = default;

QCNG::~QCNG() noexcept = default;

QCNG::~QCNG()
QCNG::Status QCNG::login(const TokenData &token)
{
delete d;
std::unique_ptr<Private> p = std::make_unique<Private>();
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<DWORD>(), 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 = std::move(p);
return PinOK;
}

QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const
Expand Down Expand Up @@ -155,33 +164,24 @@ QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray
template<typename F>
QByteArray QCNG::exec(F &&func) const
{
d->err = UnknownError;
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()),
d->token.data(u"spec"_s).value<DWORD>(), 0)))
if (!d)
return {};
// https://docs.microsoft.com/en-us/archive/blogs/alejacma/smart-cards-pin-gets-cached
NCryptSetProperty(key, NCRYPT_PIN_PROPERTY, nullptr, 0, 0);
status = UnknownError;
QByteArray result;
switch(func(prov, key, result))
switch(func(d->prov, d->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::PinStatus QCNG::lastError() const { return d->err; }

QList<TokenData> QCNG::tokens() const
QList<TokenData> QCNG::tokens()
{
QList<TokenData> result;
auto prop = [](NCRYPT_HANDLE handle, LPCWSTR param) -> QByteArray {
Expand Down Expand Up @@ -275,12 +275,6 @@ QList<TokenData> QCNG::tokens() const
return result;
}

QCNG::PinStatus QCNG::login(const TokenData &token)
{
d->token = token;
return d->err = QCNG::PinOK;
}

QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const
{
return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) {
Expand All @@ -301,7 +295,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 && d->pss)
{
padding = BCRYPT_PAD_PSS;
paddingInfo = &rsaPSS;
Expand Down
Loading
Loading