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
91 changes: 53 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 @@ -110,43 +110,55 @@ getDecryptStatus(const std::vector<uint8_t>& result, QCryptoBackend::PinStatus p
}

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.value())
return getDecryptStatus(val.error());
backend.reset(val.value());
}
QByteArray decryptedKey = backend->decrypt(toByteArray(data), oaep);
dst.assign(decryptedKey.cbegin(), decryptedKey.cend());
backend.reset();
return (dst.empty() ? BACKEND_ERROR : libcdoc::OK);
}

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.value())
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);
backend.reset();
return (dst.empty() ? BACKEND_ERROR : libcdoc::OK);
}

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.value())
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());
backend.reset();
return (dst.empty() ? BACKEND_ERROR : libcdoc::OK);
}

libcdoc::result_t
Expand Down Expand Up @@ -281,32 +293,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 +327,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(backend.release());

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(QCryptoBackend *backend) {
this->backend.reset(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
1 change: 1 addition & 0 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
43 changes: 16 additions & 27 deletions client/QCNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,18 @@ struct SCOPE
constexpr T* operator&() noexcept { return &d; }
};

class QCNG::Private
QCNG::QCNG()
{
public:
TokenData token;
QCNG::PinStatus err = QCNG::PinOK;
};

QCNG::QCNG( QObject *parent )
: QCryptoBackend(parent)
, d(new Private)
{}
}

QCNG::~QCNG()
{
delete d;
}

QCNG::Status QCNG::login(const TokenData &_token)
{
token = _token;
return PinOK;
}

QByteArray QCNG::decrypt(const QByteArray &data, bool oaep) const
Expand Down Expand Up @@ -155,33 +152,31 @@ QByteArray QCNG::deriveHMACExtract(const QByteArray &publicKey, const QByteArray
template<typename F>
QByteArray QCNG::exec(F &&func) const
{
d->err = UnknownError;
status = UnknownError;
SCOPE<NCRYPT_PROV_HANDLE> 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<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(FAILED(NCryptOpenKey(prov, &key, LPWSTR(token.data(u"key"_s).toString().utf16()),
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))
{
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 +270,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 +290,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;
Expand Down
15 changes: 7 additions & 8 deletions client/QCNG.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,32 @@
#pragma once

#include "QCryptoBackend.h"
#include "TokenData.h"

#include <qt_windows.h>
#include <ncrypt.h>

class QCNG final: public QCryptoBackend
{
Q_OBJECT
public:
explicit QCNG(QObject *parent = nullptr);
explicit QCNG();
~QCNG() final;

QList<TokenData> tokens() const final;
Status login(const TokenData &token) final;
void logout() 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;
PinStatus lastError() const final;
PinStatus login(const TokenData &token) final;
void logout() final {}
QByteArray sign(QCryptographicHash::Algorithm type, const QByteArray &digest) const final;

static QList<TokenData> tokens();
private:
template<typename F>
QByteArray derive(const QByteArray &publicKey, F &&func) const;
template<typename F>
QByteArray exec(F &&func) const;

class Private;
Private *d;
TokenData token;
};
Loading
Loading