Skip to content

Commit

Permalink
Workaround Latvia driver not supporting PSS padding (open-eid#1170)
Browse files Browse the repository at this point in the history
IB-7609

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma authored Apr 26, 2023
1 parent 0c9f5d5 commit f4996a0
Showing 1 changed file with 61 additions and 54 deletions.
115 changes: 61 additions & 54 deletions client/QCNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@
#include <common/QPCSC.h>

#include <QtCore/QDebug>
#include <QtNetwork/QSslKey>

template<typename T, typename D = decltype(NCryptFreeObject)>
struct SCOPE
{
T d {};
~SCOPE() { if(d) D(d); }
inline operator T() const { return d; }
inline T* operator&() { return &d; }
};

class QCNG::Private
{
Expand All @@ -46,7 +56,7 @@ QCNG::~QCNG()
QByteArray QCNG::decrypt(const QByteArray &data) const
{
return exec([&](NCRYPT_PROV_HANDLE prov, NCRYPT_KEY_HANDLE key, QByteArray &result) {
DWORD size = DWORD(data.size());
auto size = DWORD(data.size());
result.resize(int(size));
SECURITY_STATUS err = NCryptDecrypt(key, PBYTE(data.constData()), DWORD(data.size()), nullptr,
PBYTE(result.data()), DWORD(result.size()), &size, NCRYPT_PAD_PKCS1_FLAG);
Expand All @@ -68,18 +78,14 @@ QByteArray QCNG::derive(const QByteArray &publicKey, std::function<long (NCRYPT_
}
QByteArray blob((char*)&oh, sizeof(BCRYPT_ECCKEY_BLOB));
blob += publicKey.mid(1);
NCRYPT_KEY_HANDLE publicKeyHandle = 0;
NCRYPT_SECRET_HANDLE sharedSecret = 0;
SECURITY_STATUS err = 0;
SCOPE<NCRYPT_KEY_HANDLE> publicKeyHandle;
SCOPE<NCRYPT_SECRET_HANDLE> sharedSecret;
SECURITY_STATUS err {};
if(SUCCEEDED(err = NCryptImportKey(prov, NULL, BCRYPT_ECCPUBLIC_BLOB, nullptr, &publicKeyHandle, PBYTE(blob.data()), DWORD(blob.size()), 0)))
err = NCryptSecretAgreement(key, publicKeyHandle, &sharedSecret, 0);
if(publicKeyHandle)
NCryptFreeObject(publicKeyHandle);
if(FAILED(err))
return err;
err = func(sharedSecret, derived);
NCryptFreeObject(sharedSecret);
return err;
return func(sharedSecret, derived);
});
}

Expand All @@ -102,11 +108,9 @@ QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash
paramValues.push_back({ULONG(sizeof(BCRYPT_SHA512_ALGORITHM)), KDF_HASH_ALGORITHM, PBYTE(BCRYPT_SHA512_ALGORITHM)}); break;
default: return NTE_INVALID_PARAMETER;
}
BCryptBufferDesc params{ BCRYPTBUFFER_VERSION };
params.cBuffers = ULONG(paramValues.size());
params.pBuffers = paramValues.data();
DWORD size = 0;
SECURITY_STATUS err = 0;
BCryptBufferDesc params{ BCRYPTBUFFER_VERSION, ULONG(paramValues.size()), paramValues.data() };
DWORD size {};
SECURITY_STATUS err {};
if(FAILED(err = NCryptDeriveKey(sharedSecret, BCRYPT_KDF_SP80056A_CONCAT, &params, nullptr, 0, &size, 0)))
return err;
derived.resize(int(size));
Expand All @@ -119,23 +123,17 @@ QByteArray QCNG::deriveConcatKDF(const QByteArray &publicKey, QCryptographicHash
QByteArray QCNG::exec(std::function<long (NCRYPT_PROV_HANDLE, NCRYPT_KEY_HANDLE, QByteArray &)> &&func) const
{
d->err = UnknownError;
NCRYPT_PROV_HANDLE prov = 0;
SCOPE<NCRYPT_PROV_HANDLE> prov;
if(FAILED(NCryptOpenStorageProvider(&prov, LPCWSTR(d->token.data(QStringLiteral("provider")).toString().utf16()), 0)))
return {};
NCRYPT_KEY_HANDLE key = 0;
SCOPE<NCRYPT_KEY_HANDLE> key;
if(FAILED(NCryptOpenKey(prov, &key, LPWSTR(d->token.data(QStringLiteral("key")).toString().utf16()),
d->token.data(QStringLiteral("spec")).value<DWORD>(), 0)))
{
NCryptFreeObject(prov);
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;
long err = func(prov, key, result);
NCryptFreeObject(key);
NCryptFreeObject(prov);
switch(err)
switch(func(prov, key, result))
{
case ERROR_SUCCESS:
d->err = PinOK;
Expand All @@ -156,60 +154,69 @@ QList<TokenData> QCNG::tokens() const
auto prop = [](NCRYPT_HANDLE handle, LPCWSTR param) -> QByteArray {
if(!handle)
return {};
DWORD size = 0;
DWORD size {};
if(NCryptGetProperty(handle, param, nullptr, 0, &size, 0))
return {};
QByteArray data(int(size), '\0');
if(NCryptGetProperty(handle, param, PBYTE(data.data()), size, &size, 0))
data.clear();
return {};
return data;
};
auto enumKeys = [&result, &prop](const QString &provider, const QString &reader = {}) {
QString scope = QStringLiteral(R"(\\.\%1\)").arg(reader);
NCRYPT_PROV_HANDLE h = 0;
SCOPE<NCRYPT_PROV_HANDLE> h;
SECURITY_STATUS err = NCryptOpenStorageProvider(&h, LPCWSTR(provider.utf16()), 0);
NCryptKeyName *keyname {};
PVOID pos {};
BCRYPT_PSS_PADDING_INFO rsaPSS { NCRYPT_SHA256_ALGORITHM, 32 };
DWORD size = 0;
DWORD size {};
QByteArray digest = QCryptographicHash::hash({0}, QCryptographicHash::Sha256);
while(SUCCEEDED(NCryptEnumKeys(h, reader.isEmpty() ? nullptr : LPCWSTR(scope.utf16()), &keyname, &pos, NCRYPT_SILENT_FLAG)))
{
NCRYPT_KEY_HANDLE key = 0;
SCOPE<NCryptKeyName*,decltype(NCryptFreeBuffer)> keyname_scope{keyname};
SCOPE<NCRYPT_KEY_HANDLE> key;
err = NCryptOpenKey(h, &key, keyname->pszName, keyname->dwLegacyKeySpec, NCRYPT_SILENT_FLAG);
SslCertificate cert(prop(key, NCRYPT_CERTIFICATE_PROPERTY), QSsl::Der);
if(!cert.isNull())
if(cert.isNull())
continue;

QString guid = prop(h, NCRYPT_SMARTCARD_GUID_PROPERTY).trimmed();
TokenData &t = result.emplaceBack();
t.setReader(reader);
t.setCard(cert.type() & SslCertificate::EstEidType || cert.type() & SslCertificate::DigiIDType ?
guid : cert.subjectInfo(QSslCertificate::CommonName) + "-" + cert.serialNumber());
t.setCert(cert);
t.setData(QStringLiteral("provider"), provider);
t.setData(QStringLiteral("key"), QString::fromWCharArray(keyname->pszName));
t.setData(QStringLiteral("spec"), QVariant::fromValue(keyname->dwLegacyKeySpec));
qWarning() << "key" << t.data(QStringLiteral("provider"))
<< "spec" << t.data(QStringLiteral("spec"))
<< "alg" << QString::fromWCharArray(keyname->pszAlgid)
<< "flags" << keyname->dwFlags;
if(cert.publicKey().algorithm() != QSsl::Rsa)
continue;

static const QHash<QByteArray,bool> supportsPSS {
{"3BDD18008131FE45904C41545649412D65494490008C", false}, // LV-G1
{"3BDB960080B1FE451F830012428F536549440F900020", false}, // LV-G2
};
QByteArray atr = QPCSCReader(reader, &QPCSC::instance()).atr();
if(supportsPSS.contains(atr))
{
QString guid = prop(h, NCRYPT_SMARTCARD_GUID_PROPERTY).trimmed();
TokenData t;
t.setReader(reader);
t.setCard(cert.type() & SslCertificate::EstEidType || cert.type() & SslCertificate::DigiIDType ?
guid : cert.subjectInfo(QSslCertificate::CommonName) + "-" + cert.serialNumber());
t.setCert(cert);
t.setData(QStringLiteral("provider"), provider);
t.setData(QStringLiteral("key"), QString::fromWCharArray(keyname->pszName));
t.setData(QStringLiteral("spec"), QVariant::fromValue(keyname->dwLegacyKeySpec));
qWarning() << "key" << t.data(QStringLiteral("provider"))
<< "spec" << t.data(QStringLiteral("spec"))
<< "alg" << QString::fromWCharArray(keyname->pszAlgid)
<< "flags" << keyname->dwFlags;
SECURITY_STATUS err = NCryptSignHash(key, &rsaPSS, PBYTE(digest.data()), DWORD(digest.size()),
nullptr, 0, &size, BCRYPT_PAD_PSS);
t.setData(QStringLiteral("PSS"), SUCCEEDED(err));
result << t;
t.setData(QStringLiteral("PSS"), supportsPSS.value(atr));
continue;
}
NCryptFreeObject(key);
NCryptFreeBuffer(keyname);
keyname = nullptr;
SECURITY_STATUS err = NCryptSignHash(key, &rsaPSS, PBYTE(digest.data()), DWORD(digest.size()),
nullptr, 0, &size, BCRYPT_PAD_PSS);
t.setData(QStringLiteral("PSS"), SUCCEEDED(err));
}
NCryptFreeObject(h);
};

qWarning() << "Start enumerationg providers";
DWORD count = 0;
DWORD count {};
NCryptProviderName *providers {};
NCryptEnumStorageProviders(&count, &providers, NCRYPT_SILENT_FLAG);
for( DWORD i = 0; i < count; ++i )
for(DWORD i {}; i < count; ++i)
{
QString provider = QString::fromWCharArray(providers[i].pszName);
qWarning() << "Found provider" << provider;
Expand Down Expand Up @@ -249,12 +256,12 @@ QByteArray QCNG::sign(QCryptographicHash::Algorithm type, const QByteArray &dige
default: return NTE_INVALID_PARAMETER;
}
BCRYPT_PKCS1_PADDING_INFO rsaPKCS1 { rsaPSS.pszAlgId };
DWORD size = 0;
DWORD size {};
QString algo(5, '\0');
NCryptGetProperty(key, NCRYPT_ALGORITHM_GROUP_PROPERTY, PBYTE(algo.data()), DWORD((algo.size() + 1) * 2), &size, 0);
algo.resize(size/2 - 1);
bool isRSA = algo == QLatin1String("RSA");
DWORD padding = 0;
DWORD padding {};
PVOID paddingInfo {};
if(isRSA && d->token.data(QStringLiteral("PSS")).toBool())
{
Expand Down

0 comments on commit f4996a0

Please sign in to comment.