@@ -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
@@ -103,23 +104,17 @@ QPCSCReader::Result Card::transfer(QPCSCReader *reader, bool verify, const QByte
103104}
104105
105106
106- QByteArrayView Card::parseFCI (const QByteArray & data, quint8 expectedTag)
107+ QByteArrayView Card::parseFCI (QByteArrayView data, quint8 expectedTag)
107108{
108- for (auto i = data.constBegin (); i != data.constEnd (); ++i )
109+ for (auto i = data.begin (); i != data.end ();)
109110 {
110- quint8 tag (*i), size (*++i );
111+ quint8 tag (*i++ ), size (*i++ );
111112 if (tag == expectedTag)
112- return QByteArrayView (i + 1 , size);
113- switch (tag)
114- {
115- case 0x6F :
116- case 0x62 :
117- case 0x64 :
118- case 0xA1 : continue ;
119- default : i += size; break ;
120- }
113+ return {i, size};
114+ if ((tag & 0x20 ) == 0 )
115+ i += size;
121116 }
122- return QByteArrayView () ;
117+ return {} ;
123118}
124119
125120
@@ -291,6 +286,144 @@ bool IDEMIACard::updateCounters(QPCSCReader *reader, QSmartCardDataPrivate *d) c
291286
292287
293288
289+ const QByteArray THALESCard::AID = APDU(" 00A4040C 0C A000000063504B43532D3135" );
290+
291+ QPCSCReader::Result THALESCard::change (QPCSCReader *reader, QSmartCardData::PinType type, const QString &pin_, const QString &newpin_) const
292+ {
293+ QByteArray cmd = CHANGE;
294+ QByteArray newpin = pinTemplate (newpin_);
295+ QByteArray pin = pinTemplate (pin_);
296+ cmd[3 ] = char (0x80 | type);
297+ cmd[4 ] = char (pin.size () + newpin.size ());
298+ return transfer (reader, false , cmd + pin + newpin, type, quint8 (pin.size ()), true );
299+ }
300+
301+ bool THALESCard::isSupported (const QByteArray &atr)
302+ {
303+ return atr == " 3BFF9600008031FE438031B85365494464B085051012233F1D" ;
304+ }
305+
306+ bool THALESCard::loadPerso (QPCSCReader *reader, QSmartCardDataPrivate *d) const
307+ {
308+ d->pukReplacable = false ;
309+ if (d->data .isEmpty () && reader->transfer (APDU (" 00A4080C 02 DFDD" )))
310+ {
311+ QByteArray cmd = APDU (" 00A4020C 02 5001" );
312+ for (char data = 1 ; data <= 8 ; ++data)
313+ {
314+ cmd[6 ] = data;
315+ if (!reader->transfer (cmd))
316+ return false ;
317+ QPCSCReader::Result result = reader->transfer (READBINARY);
318+ if (!result)
319+ return false ;
320+ QString record = QString::fromUtf8 (result.data .trimmed ());
321+ if (record == QChar (0 ))
322+ record.clear ();
323+ switch (data)
324+ {
325+ case QSmartCardData::SurName:
326+ case QSmartCardData::FirstName:
327+ case QSmartCardData::Citizen:
328+ case QSmartCardData::Id:
329+ case QSmartCardData::DocumentId:
330+ d->data [QSmartCardData::PersonalDataType (data)] = record;
331+ break ;
332+ case QSmartCardData::BirthDate:
333+ if (!record.isEmpty ())
334+ d->data [QSmartCardData::BirthDate] = QDate::fromString (record.left (10 ), QStringLiteral (" dd MM yyyy" ));
335+ break ;
336+ case QSmartCardData::Expiry:
337+ d->data [QSmartCardData::Expiry] = QDateTime::fromString (record, QStringLiteral (" dd MM yyyy" )).addDays (1 ).addSecs (-1 );
338+ break ;
339+ default : break ;
340+ }
341+ }
342+ }
343+
344+ bool readFailed = false ;
345+ auto readCert = [&](const QByteArray &path) {
346+ QPCSCReader::Result data = reader->transfer (path);
347+ if (!data)
348+ {
349+ readFailed = true ;
350+ return QSslCertificate ();
351+ }
352+ auto sizeTag = parseFCI (data.data , 0x81 );
353+ if (sizeTag.isEmpty ())
354+ return QSslCertificate ();
355+ QByteArray cert;
356+ QByteArray cmd = READBINARY;
357+ for (int size = quint8 (sizeTag[0 ]) << 8 | quint8 (sizeTag[1 ]); cert.size () < size;)
358+ {
359+ cmd[2 ] = char (cert.size () >> 8 );
360+ cmd[3 ] = char (cert.size ());
361+ data = reader->transfer (cmd);
362+ if (!data)
363+ {
364+ readFailed = true ;
365+ return QSslCertificate ();
366+ }
367+ cert += data.data ;
368+ }
369+ return QSslCertificate (cert, QSsl::Der);
370+ };
371+ if (d->authCert .isNull ())
372+ d->authCert = readCert (APDU (" 00A40804 04 ADF1 3411 00" ));
373+ if (d->signCert .isNull ())
374+ d->signCert = readCert (APDU (" 00A40804 04 ADF2 3421 00" ));
375+
376+ if (readFailed)
377+ return false ;
378+ return updateCounters (reader, d);
379+ }
380+
381+ QByteArray THALESCard::pinTemplate (const QString &pin)
382+ {
383+ QByteArray result = pin.toUtf8 ();
384+ result += QByteArray (12 - result.size (), char (0x00 ));
385+ return result;
386+ }
387+
388+ QPCSCReader::Result THALESCard::replace (QPCSCReader *reader, QSmartCardData::PinType type, const QString &puk_, const QString &pin_) const
389+ {
390+ QByteArray puk = pinTemplate (puk_);
391+ QByteArray pin = pinTemplate (pin_);
392+ QByteArray cmd = REPLACE;
393+ cmd[3 ] = char (0x80 | type);
394+ cmd[4 ] = char (puk.size () + pin.size ());
395+ return transfer (reader, false , cmd + puk + pin, type, quint8 (puk.size ()), true );
396+ }
397+
398+ QByteArray THALESCard::sign (QPCSCReader *reader, const QByteArray &dgst) const
399+ {
400+ if (!reader->transfer (APDU (" 002241B6 09 800154840101" )))
401+ return {};
402+
403+ QByteArray send {0x90 , char (dgst.size ())};
404+ send.append (dgst);
405+ send.insert (0 , char (send.size ()));
406+ send.insert (0 , APDU (" 002A90A0" ));
407+
408+ if (!reader->transfer (send))
409+ return {};
410+
411+ return reader->transfer (APDU (" 002A9E9A 00" )).data ;
412+ }
413+
414+ bool THALESCard::updateCounters (QPCSCReader *reader, QSmartCardDataPrivate *d) const
415+ {
416+ if (auto data = reader->transfer (APDU (" 00CB00FF 05 A003830181 00" )))
417+ d->retry [QSmartCardData::Pin1Type] = quint8 (data.data [14 ]);
418+ if (auto data = reader->transfer (APDU (" 00CB00FF 05 A003830183 00" )))
419+ d->retry [QSmartCardData::PukType] = quint8 (data.data [14 ]);
420+ if (auto data = reader->transfer (APDU (" 00CB00FF 05 A003830182 00" )))
421+ d->retry [QSmartCardData::Pin2Type] = quint8 (data.data [14 ]);
422+ return true ;
423+ }
424+
425+
426+
294427QSharedPointer<QPCSCReader> QSmartCard::Private::connect (const QString &reader)
295428{
296429 qCDebug (CLog) << " Connecting to reader" << reader;
@@ -315,7 +448,8 @@ QSmartCard::ErrorType QSmartCard::Private::handlePinResult(QPCSCReader *reader,
315448 case 0x6401 : return QSmartCard::CancelError; // Cancel (OK, SCM)
316449 case 0x6402 : return QSmartCard::DifferentError;
317450 case 0x6403 : return QSmartCard::LenghtError;
318- case 0x6983 : return QSmartCard::BlockedError;
451+ case 0x6983 :
452+ case 0x6984 : return QSmartCard::BlockedError;
319453 case 0x6985 :
320454 case 0x6A80 : return QSmartCard::OldNewPinSameError;
321455 default : return QSmartCard::UnknownError;
@@ -450,7 +584,12 @@ void QSmartCard::reloadCard(const TokenData &token, bool reloadCounters)
450584 if (!selectedReader->connect () || !selectedReader->beginTransaction ())
451585 return ;
452586
453- if (!IDEMIACard::isSupported (selectedReader->atr ())) {
587+ std::unique_ptr<Card> card;
588+ if (IDEMIACard::isSupported (selectedReader->atr ()))
589+ card = std::make_unique<IDEMIACard>();
590+ else if (THALESCard::isSupported (selectedReader->atr ()))
591+ card = std::make_unique<THALESCard>();
592+ else {
454593 qDebug () << " Unsupported card" ;
455594 return ;
456595 }
@@ -460,7 +599,7 @@ void QSmartCard::reloadCard(const TokenData &token, bool reloadCounters)
460599 t = d->t .d ;
461600 t->reader = selectedReader->name ();
462601 t->pinpad = selectedReader->isPinPad ();
463- d->card = std::make_unique<IDEMIACard>( );
602+ d->card = std::move (card );
464603 if (d->card ->loadPerso (selectedReader.data (), t))
465604 {
466605 d->t .d = std::move (t);
0 commit comments