Skip to content
Merged
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
1 change: 0 additions & 1 deletion client/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ void Diagnostics::generalInfo(QTextStream &s)
continue;

s << "ATR - " << reader.atr() << "<br />";
reader.beginTransaction();
constexpr auto APDU = &QByteArray::fromHex;
auto printAID = [&](QLatin1String label, const QByteArray &apdu)
{
Expand Down
28 changes: 11 additions & 17 deletions client/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,16 @@ MainWindow::MainWindow( QWidget *parent )

// Refresh ID card info in card widget
connect(qApp->signer(), &QSigner::cacheChanged, this, &MainWindow::updateSelector);
connect(&QPCSC::instance(), &QPCSC::statusChanged, this, &MainWindow::updateSelector);
connect(qApp->signer(), &QSigner::signDataChanged, this, [this](const TokenData &token) {
updateSelectorData(token);
updateSelector();
updateMyEID(token);
ui->signContainerPage->cardChanged(token.cert(), token.data(QStringLiteral("blocked")).toBool());
});
connect(qApp->signer(), &QSigner::authDataChanged, this, [this](const TokenData &token) {
updateSelectorData(token);
updateSelector();
updateMyEID(token);
ui->cryptoContainerPage->cardChanged(token.cert(), token.data(QStringLiteral("blocked")).toBool());
});
QPCSC::instance().start();

// Refresh card info on "My EID" page
connect(qApp->signer()->smartcard(), &QSmartCard::dataChanged, this, &MainWindow::updateMyEid);
Expand All @@ -124,7 +122,7 @@ MainWindow::MainWindow( QWidget *parent )
connect(ui->accordion, &Accordion::changePinClicked, this, &MainWindow::changePinClicked);
connect(ui->cardInfo, &CardWidget::selected, ui->selector, &QToolButton::toggle);

updateSelectorData(qApp->signer()->tokensign());
updateSelector();
updateMyEID(qApp->signer()->tokensign());
ui->signContainerPage->cardChanged(qApp->signer()->tokensign().cert());
ui->cryptoContainerPage->cardChanged(qApp->signer()->tokenauth().cert());
Expand Down Expand Up @@ -846,11 +844,7 @@ bool MainWindow::wrapContainer(bool signing)

void MainWindow::updateSelector()
{
updateSelectorData({});
}

void MainWindow::updateSelectorData(TokenData data)
{
TokenData selected;
enum Filter: uint8_t {
Signing,
Decrypting,
Expand All @@ -860,24 +854,24 @@ void MainWindow::updateSelectorData(TokenData data)
{
case SignIntro:
case SignDetails:
if(data.isNull()) data = qApp->signer()->tokensign();
selected = qApp->signer()->tokensign();
filter = Signing;
break;
case CryptoIntro:
case CryptoDetails:
if(data.isNull()) data = qApp->signer()->tokenauth();
selected = qApp->signer()->tokenauth();
filter = Decrypting;
break;
case MyEid:
default:
if(data.isNull()) data = qApp->signer()->smartcard()->tokenData();
selected = qApp->signer()->smartcard()->tokenData();
filter = MyEID;
break;
}
QVector<TokenData> list;
for(const TokenData &token: qApp->signer()->cache())
{
if(token.card() == data.card())
if(token.card() == selected.card())
continue;
if(std::any_of(list.cbegin(), list.cend(), [token](const TokenData &item) { return token.card() == item.card(); }))
continue;
Expand All @@ -891,12 +885,12 @@ void MainWindow::updateSelectorData(TokenData data)
continue;
list.append(token);
}
ui->noCardInfo->setVisible(ui->cardInfo->token().isNull());
ui->noCardInfo->setVisible(selected.isNull());
ui->selector->setHidden(list.isEmpty());
ui->selector->setChecked(false);
ui->cardInfo->setVisible(ui->noCardInfo->isHidden());
ui->cardInfo->setCursor(ui->selector->isVisible() ? Qt::PointingHandCursor : Qt::ArrowCursor);
ui->cardInfo->update(data, list.size() > 1);
ui->cardInfo->update(selected, list.size() > 1);
if (!QPCSC::instance().serviceRunning())
ui->noCardInfo->update(NoCardInfo::NoPCSC);
else if(QPCSC::instance().readers().isEmpty())
Expand All @@ -910,7 +904,7 @@ void MainWindow::updateSelectorData(TokenData data)
if(show)
{
auto *cardPopup = new CardPopup(list, this);
connect(cardPopup, &CardPopup::activated, qApp->signer(), &QSigner::selectCard, Qt::QueuedConnection);
connect(cardPopup, &CardPopup::activated, qApp->signer(), &QSigner::selectCard);
connect(cardPopup, &CardPopup::activated, this, [this] { ui->selector->setChecked(false); });
cardPopup->show();
}
Expand Down
1 change: 0 additions & 1 deletion client/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ class MainWindow final : public QWidget
void sign(F &&sign);
bool validateFiles(const QString &container, const QStringList &files);
void updateSelector();
void updateSelectorData(TokenData data);
void updateMyEID(const TokenData &t);
void updateMyEid(const QSmartCardData &data);
bool wrap(const QString& wrappedFile, bool enclose);
Expand Down
9 changes: 7 additions & 2 deletions client/QCNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
#include "SslCertificate.h"
#include "TokenData.h"

#include <QtCore/QUuid>
#include <QtCore/QLoggingCategory>
#include <QtCore/QRegularExpression>
#include <QtNetwork/QSslKey>

using namespace Qt::Literals::StringLiterals;
Expand Down Expand Up @@ -220,7 +222,9 @@ QList<TokenData> QCNG::tokens() const
if(QByteArray tmp = prop(key, NCRYPT_READER_PROPERTY); !tmp.isEmpty())
reader = QString::fromUtf16((const char16_t*)tmp.data());
}
QString guid = prop(h, NCRYPT_SMARTCARD_GUID_PROPERTY).trimmed();
QByteArray guidData = prop(h, NCRYPT_SMARTCARD_GUID_PROPERTY);
static const QRegularExpression reg(QStringLiteral("\\w{1,2}\\d{7} *"));
QString guid = reg.match(guidData).hasMatch() ? guidData.trimmed() : QUuid(*((GUID*)guidData.data())).toString(QUuid::WithBraces);
TokenData &t = result.emplaceBack();
t.setReader(reader);
t.setCard(cert.type() & SslCertificate::EstEidType || cert.type() & SslCertificate::DigiIDType ?
Expand All @@ -232,7 +236,8 @@ QList<TokenData> QCNG::tokens() const
qCWarning(CNG) << "key" << t.data(u"provider"_s)
<< "spec" << t.data(u"spec"_s)
<< "alg" << QStringView(keyname->pszAlgid)
<< "flags" << keyname->dwFlags;
<< "flags" << keyname->dwFlags
<< t.card();
if(cert.publicKey().algorithm() != QSsl::Rsa || reader.isEmpty())
continue;

Expand Down
84 changes: 33 additions & 51 deletions client/QPCSC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ QHash<DRIVER_FEATURES,quint32> QPCSCReader::Private::features()
for(const auto &f: feature)
{
if(f.tag)
featuresList[DRIVER_FEATURES(f.tag)] = qFromBigEndian<quint32>(f.value);
featuresList[f.tag] = qFromBigEndian<quint32>(f.value);
}
return featuresList;
}
Expand All @@ -94,10 +94,12 @@ QPCSC::QPCSC()
QPCSC::~QPCSC()
{
requestInterruption();
d->sleepCond.wakeAll();
if(d->thread)
SC(Cancel, d->thread);
wait();
if( d->context )
if(d->context)
SC(ReleaseContext, d->context);
qDeleteAll(d->lock);
delete d;
}

Expand Down Expand Up @@ -138,23 +140,30 @@ void QPCSC::run()
std::vector<SCARD_READERSTATE> list;
while(!isInterruptionRequested())
{
if(!pcsc.serviceRunning())
{
sleep(5);
continue;
}
// "\\?PnP?\Notification" does not work on macOS
QByteArray data = pcsc.rawReaders();
if(data.isEmpty())
{
sleep(5);
QMutexLocker locker(&d->sleepMutex);
if (isInterruptionRequested())
break;
d->sleepCond.wait(&d->sleepMutex, 5000);
continue;
}
for(const char *name = data.constData(); *name; name += strlen(name) + 1)
{
if(std::none_of(list.cbegin(), list.cend(), [&name](const SCARD_READERSTATE &state) { return strcmp(state.szReader, name) == 0; }))
list.push_back({ strdup(name), nullptr, 0, 0, 0, {} });
}
if(list.empty())
{
QMutexLocker locker(&d->sleepMutex);
if (isInterruptionRequested())
break;
d->sleepCond.wait(&d->sleepMutex, 5000);
continue;
}
d->thread = pcsc.d->context;
if(SC(GetStatusChange, pcsc.d->context, 5*1000U, list.data(), DWORD(list.size())) != SCARD_S_SUCCESS)
continue;
for(auto i = list.begin(); i != list.end(); )
Expand All @@ -164,6 +173,8 @@ void QPCSC::run()
++i;
continue;
}
if((i->dwCurrentState & SCARD_STATE_PRESENT) != (i->dwEventState & SCARD_STATE_PRESENT))
Q_EMIT cardChanged();
i->dwCurrentState = i->dwEventState;
qCDebug(SCard) << "New state: " << QString::fromLocal8Bit(i->szReader) << stateToString(i->dwCurrentState);
Q_EMIT statusChanged(QString::fromLocal8Bit(i->szReader), stateToString(i->dwCurrentState));
Expand All @@ -176,6 +187,7 @@ void QPCSC::run()
++i;
}
}
d->thread = {};
}

bool QPCSC::serviceRunning() const
Expand All @@ -191,19 +203,19 @@ bool QPCSC::serviceRunning() const
QPCSCReader::QPCSCReader( const QString &reader, QPCSC *parent )
: d(new Private)
{
if(!parent->d->lock.contains(reader))
parent->d->lock[reader] = new QMutex();
parent->d->lock[reader]->lock();
d->d = parent->d;
d->reader = reader.toUtf8();
d->state.szReader = d->reader.constData();
updateState();
if(parent->d->context)
SC(GetStatusChange, d->d->context, 0, &d->state, 1U);
}

QPCSCReader::~QPCSCReader()
{
disconnect();
d->d->lock[d->reader]->unlock();
if(d->isTransacted)
SC(EndTransaction, d->card, SCARD_LEAVE_CARD);
if(d->card)
SC(Disconnect, d->card, SCARD_LEAVE_CARD);
delete d;
}

Expand All @@ -212,29 +224,12 @@ QByteArray QPCSCReader::atr() const
return QByteArray::fromRawData((const char*)d->state.rgbAtr, int(d->state.cbAtr)).toHex().toUpper();
}

bool QPCSCReader::beginTransaction()
{
return d->isTransacted = SC(BeginTransaction, d->card) == SCARD_S_SUCCESS;
}

bool QPCSCReader::connect(Connect connect, Mode mode)
{
LONG err = SC(Connect, d->d->context, d->state.szReader, connect, mode, &d->card, &d->io.dwProtocol);
updateState();
return err == SCARD_S_SUCCESS;
}

void QPCSCReader::disconnect( Reset reset )
{
if(d->isTransacted)
SC(EndTransaction, d->card, reset);
d->isTransacted = false;
if( d->card )
SC(Disconnect, d->card, reset);
d->io.dwProtocol = SCARD_PROTOCOL_UNDEFINED;
d->card = {};
d->featuresList.clear();
updateState();
if(SC(Connect, d->d->context, d->state.szReader, connect, mode, &d->card, &d->io.dwProtocol) != SCARD_S_SUCCESS)
return false;
d->isTransacted = SC(BeginTransaction, d->card) == SCARD_S_SUCCESS;
return true;
}

bool QPCSCReader::isPinPad() const
Expand Down Expand Up @@ -272,7 +267,7 @@ QHash<QPCSCReader::Properties, int> QPCSCReader::properties() const
int tag = *p++, len = *p++, value = 0;
for(int i = 0; i < len; ++i)
value |= *p++ << 8 * i;
properties[Properties(tag)] = value;
properties.emplace(Properties(tag), value);
}
}
return properties;
Expand Down Expand Up @@ -303,7 +298,7 @@ QPCSCReader::Result QPCSCReader::transfer( const QByteArray &apdu ) const
case 0x6100: // Read more
{
QByteArray cmd( "\x00\xC0\x00\x00\x00", 5 );
cmd[4] = data.at(int(size - 1));
cmd[4] = char(result.SW);
Result result2 = transfer( cmd );
result2.data.prepend(result.data);
return result2;
Expand Down Expand Up @@ -397,16 +392,3 @@ QPCSCReader::Result QPCSCReader::transferCTL(const QByteArray &apdu, bool verify
if(!result.data.isEmpty()) qCDebug(APDU).nospace().noquote() << result.data.toHex();
return result;
}

bool QPCSCReader::updateState( quint32 msec )
{
if(!d->d->context)
return false;
d->state.dwCurrentState = d->state.dwEventState;
switch(SC(GetStatusChange, d->d->context, msec, &d->state, 1U))
{
case LONG(SCARD_S_SUCCESS): return true;
case LONG(SCARD_E_TIMEOUT): return msec == 0;
default: return false;
}
}
17 changes: 7 additions & 10 deletions client/QPCSC.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class QPCSC final: public QThread

Q_SIGNALS:
void statusChanged(const QString &reader, const QStringList &state);
void cardChanged();

private:
QPCSC();
Expand All @@ -44,7 +45,7 @@ class QPCSC final: public QThread
QByteArray rawReaders() const;
void run() final;

class Private;
struct Private;
Private *d;

friend class QPCSCReader;
Expand All @@ -61,7 +62,7 @@ class QPCSCReader final: public QObject
constexpr operator bool() const { return SW == 0x9000; }
};

enum Properties {
enum Properties : quint8 {
wLcdLayout = 0x01,
bEntryValidationCondition = 0x02,
bTimeOut2 = 0x03,
Expand All @@ -76,21 +77,20 @@ class QPCSCReader final: public QObject
wIdProduct = 0x0C
};

enum Connect {
enum Connect : quint8 {
Exclusive = 1,
Shared = 2,
Direct = 3
};

enum Reset
{
enum Reset : quint8 {
LeaveCard = 0,
ResetCard = 1,
UnpowerCard = 2,
EjectCard = 3
};

enum Mode {
enum Mode : quint8 {
Undefined = 0,
T0 = 1,
T1 = 2
Expand All @@ -105,17 +105,14 @@ class QPCSCReader final: public QObject
QString name() const;
QHash<Properties,int> properties() const;
QStringList state() const;
bool updateState( quint32 msec = 0 );

bool connect( Connect connect = Shared, Mode mode = Mode(T0|T1) );
void disconnect( Reset reset = LeaveCard );
bool beginTransaction();
Result transfer( const QByteArray &apdu ) const;
Result transferCTL(const QByteArray &apdu, bool verify, quint16 lang = 0,
quint8 minlen = 4, quint8 newPINOffset = 0, bool requestCurrentPIN = true) const;

private:
Q_DISABLE_COPY(QPCSCReader)
class Private;
struct Private;
Private *d;
};
Loading
Loading