Skip to content

Commit ed55d58

Browse files
committed
Thales card support
IB-8171 Signed-off-by: Raul Metsma <[email protected]>
1 parent 59b07c1 commit ed55d58

File tree

11 files changed

+252
-46
lines changed

11 files changed

+252
-46
lines changed

client/QPCSC.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ static QStringList stateToString(DWORD state)
5353
}
5454

5555
template<auto Func, typename... Args>
56-
static auto SCCall(const char *file, int line, const char *function, Args... args)
56+
static auto SCCall(const char *file, int line, const char *function, Args&& ...args)
5757
{
58-
auto err = Func(args...);
58+
auto err = Func(std::forward<Args>(args)...);
5959
if(SCard().isDebugEnabled())
6060
QMessageLogger(file, line, function, SCard().categoryName()).debug()
6161
<< function << Qt::hex << (unsigned long)err;

client/QSmartCard.cpp

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ QString QSmartCardData::card() const { return d->card; }
5151
bool QSmartCardData::isNull() const
5252
{ return d->data.isEmpty() && d->authCert.isNull() && d->signCert.isNull(); }
5353
bool QSmartCardData::isPinpad() const { return d->pinpad; }
54+
bool QSmartCardData::isPUKReplacable() const { return d->pukReplacable; }
5455
bool QSmartCardData::isValid() const
5556
{ return d->data.value(Expiry).toDateTime() >= QDateTime::currentDateTime(); }
5657

@@ -291,6 +292,144 @@ bool IDEMIACard::updateCounters(QPCSCReader *reader, QSmartCardDataPrivate *d) c
291292

292293

293294

295+
const QByteArray THALESCard::AID = APDU("00A4040C 0C A000000063504B43532D3135");
296+
297+
QPCSCReader::Result THALESCard::change(QPCSCReader *reader, QSmartCardData::PinType type, const QString &pin_, const QString &newpin_) const
298+
{
299+
QByteArray cmd = CHANGE;
300+
QByteArray newpin = pinTemplate(newpin_);
301+
QByteArray pin = pinTemplate(pin_);
302+
cmd[3] = char(0x80 | type);
303+
cmd[4] = char(pin.size() + newpin.size());
304+
return transfer(reader, false, cmd + pin + newpin, type, quint8(pin.size()), true);
305+
}
306+
307+
bool THALESCard::isSupported(const QByteArray &atr)
308+
{
309+
return atr == "3BFF9600008031FE438031B85365494464B085051012233F1D";
310+
}
311+
312+
bool THALESCard::loadPerso(QPCSCReader *reader, QSmartCardDataPrivate *d) const
313+
{
314+
d->pukReplacable = false;
315+
if(d->data.isEmpty() && reader->transfer(APDU("00A4080C 02 DFDD")))
316+
{
317+
QByteArray cmd = APDU("00A4020C 02 5001");
318+
for(char data = 1; data <= 8; ++data)
319+
{
320+
cmd[6] = data;
321+
if(!reader->transfer(cmd))
322+
return false;
323+
QPCSCReader::Result result = reader->transfer(READBINARY);
324+
if(!result)
325+
return false;
326+
QString record = QString::fromUtf8(result.data.trimmed());
327+
if(record == QChar(0))
328+
record.clear();
329+
switch(data)
330+
{
331+
case QSmartCardData::SurName:
332+
case QSmartCardData::FirstName:
333+
case QSmartCardData::Citizen:
334+
case QSmartCardData::Id:
335+
case QSmartCardData::DocumentId:
336+
d->data[QSmartCardData::PersonalDataType(data)] = record;
337+
break;
338+
case QSmartCardData::BirthDate:
339+
if(!record.isEmpty())
340+
d->data[QSmartCardData::BirthDate] = QDate::fromString(record.left(10), QStringLiteral("dd MM yyyy"));
341+
break;
342+
case QSmartCardData::Expiry:
343+
d->data[QSmartCardData::Expiry] = QDateTime::fromString(record, QStringLiteral("dd MM yyyy")).addDays(1).addSecs(-1);
344+
break;
345+
default: break;
346+
}
347+
}
348+
}
349+
350+
bool readFailed = false;
351+
auto readCert = [&](const QByteArray &path) {
352+
QPCSCReader::Result data = reader->transfer(path);
353+
if(!data)
354+
{
355+
readFailed = true;
356+
return QSslCertificate();
357+
}
358+
auto sizeTag = parseFCI(data.data, 0x81);
359+
if(sizeTag.isEmpty())
360+
return QSslCertificate();
361+
QByteArray cert;
362+
QByteArray cmd = READBINARY;
363+
for(int size = quint8(sizeTag[0]) << 8 | quint8(sizeTag[1]); cert.size() < size;)
364+
{
365+
cmd[2] = char(cert.size() >> 8);
366+
cmd[3] = char(cert.size());
367+
data = reader->transfer(cmd);
368+
if(!data)
369+
{
370+
readFailed = true;
371+
return QSslCertificate();
372+
}
373+
cert += data.data;
374+
}
375+
return QSslCertificate(cert, QSsl::Der);
376+
};
377+
if(d->authCert.isNull())
378+
d->authCert = readCert(APDU("00A40804 04 ADF1 3411 00"));
379+
if(d->signCert.isNull())
380+
d->signCert = readCert(APDU("00A40804 04 ADF2 3421 00"));
381+
382+
if(readFailed)
383+
return false;
384+
return updateCounters(reader, d);
385+
}
386+
387+
QByteArray THALESCard::pinTemplate(const QString &pin)
388+
{
389+
QByteArray result = pin.toUtf8();
390+
result += QByteArray(12 - result.size(), char(0x00));
391+
return result;
392+
}
393+
394+
QPCSCReader::Result THALESCard::replace(QPCSCReader *reader, QSmartCardData::PinType type, const QString &puk_, const QString &pin_) const
395+
{
396+
QByteArray puk = pinTemplate(puk_);
397+
QByteArray pin = pinTemplate(pin_);
398+
QByteArray cmd = REPLACE;
399+
cmd[3] = char(0x80 | type);
400+
cmd[4] = char(puk.size() + pin.size());
401+
return transfer(reader, false, cmd + puk + pin, type, quint8(puk.size()), true);
402+
}
403+
404+
QByteArray THALESCard::sign(QPCSCReader *reader, const QByteArray &dgst) const
405+
{
406+
if(!reader->transfer(APDU("002241B6 09 800154840101")))
407+
return {};
408+
409+
QByteArray send {0x90, char(dgst.size())};
410+
send.append(dgst);
411+
send.insert(0, char(send.size()));
412+
send.insert(0, APDU("002A90A0"));
413+
414+
if(!reader->transfer(send))
415+
return {};
416+
417+
return reader->transfer(APDU("002A9E9A 00")).data;
418+
}
419+
420+
bool THALESCard::updateCounters(QPCSCReader *reader, QSmartCardDataPrivate *d) const
421+
{
422+
if(auto data = reader->transfer(APDU("00CB00FF 05 A003830181 00")))
423+
d->retry[QSmartCardData::Pin1Type] = quint8(data.data[14]);
424+
if(auto data = reader->transfer(APDU("00CB00FF 05 A003830183 00")))
425+
d->retry[QSmartCardData::PukType] = quint8(data.data[14]);
426+
if(auto data = reader->transfer(APDU("00CB00FF 05 A003830182 00")))
427+
d->retry[QSmartCardData::Pin2Type] = quint8(data.data[14]);
428+
return true;
429+
}
430+
431+
432+
294433
QSharedPointer<QPCSCReader> QSmartCard::Private::connect(const QString &reader)
295434
{
296435
qCDebug(CLog) << "Connecting to reader" << reader;
@@ -315,7 +454,8 @@ QSmartCard::ErrorType QSmartCard::Private::handlePinResult(QPCSCReader *reader,
315454
case 0x6401: return QSmartCard::CancelError; // Cancel (OK, SCM)
316455
case 0x6402: return QSmartCard::DifferentError;
317456
case 0x6403: return QSmartCard::LenghtError;
318-
case 0x6983: return QSmartCard::BlockedError;
457+
case 0x6983:
458+
case 0x6984: return QSmartCard::BlockedError;
319459
case 0x6985:
320460
case 0x6A80: return QSmartCard::OldNewPinSameError;
321461
default: return QSmartCard::UnknownError;
@@ -450,7 +590,12 @@ void QSmartCard::reloadCard(const TokenData &token, bool reloadCounters)
450590
if(!selectedReader->connect() || !selectedReader->beginTransaction())
451591
return;
452592

453-
if(!IDEMIACard::isSupported(selectedReader->atr())) {
593+
std::unique_ptr<Card> card;
594+
if(IDEMIACard::isSupported(selectedReader->atr()))
595+
card = std::make_unique<IDEMIACard>();
596+
else if(THALESCard::isSupported(selectedReader->atr()))
597+
card = std::make_unique<THALESCard>();
598+
else {
454599
qDebug() << "Unsupported card";
455600
return;
456601
}
@@ -460,7 +605,7 @@ void QSmartCard::reloadCard(const TokenData &token, bool reloadCounters)
460605
t = d->t.d;
461606
t->reader = selectedReader->name();
462607
t->pinpad = selectedReader->isPinPad();
463-
d->card = std::make_unique<IDEMIACard>();
608+
d->card = std::move(card);
464609
if(d->card->loadPerso(selectedReader.data(), t))
465610
{
466611
d->t.d = std::move(t);

client/QSmartCard.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class QSmartCardData
6262

6363
bool isNull() const;
6464
bool isPinpad() const;
65+
bool isPUKReplacable() const;
6566
bool isValid() const;
6667

6768
QVariant data( PersonalDataType type ) const;

client/QSmartCard_p.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ class IDEMIACard: public Card
6464
static const QByteArray AID, AID_OT, AID_QSCD, ATR_COSMO8, ATR_COSMOX;
6565
};
6666

67+
class THALESCard: public Card
68+
{
69+
public:
70+
QPCSCReader::Result change(QPCSCReader *reader, QSmartCardData::PinType type, const QString &pin, const QString &newpin) const final;
71+
bool loadPerso(QPCSCReader *reader, QSmartCardDataPrivate *d) const final;
72+
QPCSCReader::Result replace(QPCSCReader *reader, QSmartCardData::PinType type, const QString &puk, const QString &pin) const final;
73+
QByteArray sign(QPCSCReader *reader, const QByteArray &dgst) const final;
74+
bool updateCounters(QPCSCReader *reader, QSmartCardDataPrivate *d) const final;
75+
76+
static bool isSupported(const QByteArray &atr);
77+
static QByteArray pinTemplate(const QString &pin);
78+
79+
static const QByteArray AID;
80+
};
81+
6782
class QSmartCard::Private
6883
{
6984
public:
@@ -84,4 +99,5 @@ class QSmartCardDataPrivate: public QSharedData
8499
SslCertificate authCert, signCert;
85100
QHash<QSmartCardData::PinType,quint8> retry;
86101
bool pinpad = false;
102+
bool pukReplacable = true;
87103
};

0 commit comments

Comments
 (0)