@@ -51,6 +51,7 @@ QString QSmartCardData::card() const { return d->card; }
5151bool QSmartCardData::isNull () const
5252{ return d->data .isEmpty () && d->authCert .isNull () && d->signCert .isNull (); }
5353bool QSmartCardData::isPinpad () const { return d->pinpad ; }
54+ bool QSmartCardData::isPUKReplacable () const { return d->pukReplacable ; }
5455bool 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+
294433QSharedPointer<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);
0 commit comments