AusweisApp2/src/card/base/CardConnectionWorker.cpp

255 lines
6.4 KiB
C++

/*!
* \copyright Copyright (c) 2015 Governikus GmbH & Co. KG
*/
#include "CardConnectionWorker.h"
#include "pace/PaceHandler.h"
#include <QLoggingCategory>
using namespace governikus;
Q_DECLARE_LOGGING_CATEGORY(card)
Q_DECLARE_LOGGING_CATEGORY(support)
CardConnectionWorker::CardConnectionWorker(Reader* pReader)
: QObject()
, QEnableSharedFromThis()
, mReader(pReader)
, mSecureMessaging()
{
connect(this, &CardConnectionWorker::fireRetryCounterPotentiallyChanged, mReader.data(), &Reader::onRetryCounterPotentiallyChanged);
connect(mReader, &Reader::fireCardInserted, this, &CardConnectionWorker::onReaderInfoChanged);
connect(mReader, &Reader::fireCardRemoved, this, &CardConnectionWorker::onReaderInfoChanged);
connect(mReader, &Reader::fireCardRetryCounterChanged, this, &CardConnectionWorker::onReaderInfoChanged);
}
CardConnectionWorker::~CardConnectionWorker()
{
if (hasCard() && mReader->getCard()->isConnected())
{
mReader->getCard()->disconnect();
}
}
QSharedPointer<CardConnectionWorker> CardConnectionWorker::create(Reader* pReader)
{
return QSharedPointer<CardConnectionWorker>(new CardConnectionWorker(pReader), &QObject::deleteLater);
}
ReaderInfo CardConnectionWorker::getReaderInfo() const
{
return mReader.isNull() ? ReaderInfo() : mReader->getReaderInfo();
}
void CardConnectionWorker::setPukInoperative()
{
mReader->setPukInoperative();
}
bool CardConnectionWorker::hasCard() const
{
return !mReader.isNull() && mReader->getCard() != nullptr;
}
QSharedPointer<const EFCardAccess> CardConnectionWorker::getEfCardAccess() const
{
return getReaderInfo().getCardInfo().getEfCardAccess();
}
void CardConnectionWorker::onReaderInfoChanged(const QString& pReaderName)
{
Q_ASSERT(pReaderName == mReader->getName());
Q_EMIT fireReaderInfoChanged(mReader->getReaderInfo());
}
CardReturnCode CardConnectionWorker::transmit(const CommandApdu& pCommandApdu, ResponseApdu& pResponseApdu)
{
if (!hasCard())
{
return CardReturnCode::CARD_NOT_FOUND;
}
if (mSecureMessaging)
{
CommandApdu securedCommandApdu = mSecureMessaging->encrypt(pCommandApdu);
ResponseApdu securedResponseApdu;
CardReturnCode returnCode = mReader->getCard()->transmit(securedCommandApdu, securedResponseApdu);
if (!mSecureMessaging->decrypt(securedResponseApdu, pResponseApdu))
{
return CardReturnCode::COMMAND_FAILED;
}
return returnCode;
}
return mReader->getCard()->transmit(pCommandApdu, pResponseApdu);
}
CardReturnCode CardConnectionWorker::readFile(const FileRef& pFileRef, QByteArray& pFileContent)
{
if (!hasCard())
{
return CardReturnCode::CARD_NOT_FOUND;
}
ResponseApdu selectRes;
CommandApdu select = SelectBuilder(pFileRef).build();
CardReturnCode returnCode = transmit(select, selectRes);
if (returnCode != CardReturnCode::OK || selectRes.getReturnCode() != StatusCode::SUCCESS)
{
return CardReturnCode::COMMAND_FAILED;
}
while (true)
{
ResponseApdu res;
ReadBinaryBuilder rb(static_cast<uint>(pFileContent.count()), 0xff);
returnCode = transmit(rb.build(), res);
if (returnCode != CardReturnCode::OK)
{
break;
}
pFileContent += res.getData();
if (res.getData().size() != 0xff && res.getReturnCode() == StatusCode::END_OF_FILE)
{
return CardReturnCode::OK;
}
if (res.getReturnCode() != StatusCode::SUCCESS)
{
break;
}
}
return CardReturnCode::COMMAND_FAILED;
}
bool CardConnectionWorker::stopSecureMessaging()
{
if (mSecureMessaging.isNull())
{
return false;
}
mSecureMessaging.reset();
return true;
}
CardReturnCode CardConnectionWorker::establishPaceChannel(PACE_PIN_ID pPinId,
const QString& pPinValue,
EstablishPACEChannelOutput& pChannelOutput)
{
return establishPaceChannel(pPinId, pPinValue, nullptr, nullptr, pChannelOutput);
}
CardReturnCode CardConnectionWorker::establishPaceChannel(PACE_PIN_ID pPinId,
const QString& pPinValue,
const QByteArray& pChat,
const QByteArray& pCertificateDescription,
EstablishPACEChannelOutput& pChannelOutput)
{
if (!hasCard())
{
return CardReturnCode::CARD_NOT_FOUND;
}
CardReturnCode returnCode;
qCInfo(support) << "Starting PACE for" << pPinId;
if (mReader->getReaderInfo().isBasicReader())
{
Q_ASSERT(!pPinValue.isEmpty());
PaceHandler paceHandler(sharedFromThis());
paceHandler.setChat(pChat);
returnCode = paceHandler.establishPaceChannel(pPinId, pPinValue);
if (returnCode == CardReturnCode::OK)
{
pChannelOutput.setCarCurr(paceHandler.getCarCurr());
pChannelOutput.setCarPrev(paceHandler.getCarPrev());
pChannelOutput.setIdIcc(paceHandler.getIdIcc());
pChannelOutput.setEfCardAccess(getEfCardAccess()->getContentBytes());
pChannelOutput.setPaceReturnCode(CardReturnCode::OK);
mSecureMessaging.reset(new SecureMessaging(paceHandler.getPaceProtocol(), paceHandler.getEncryptionKey(), paceHandler.getMacKey()));
}
}
else
{
Q_ASSERT(pPinValue.isNull());
returnCode = mReader->getCard()->establishPaceChannel(pPinId, pChat, pCertificateDescription, pChannelOutput);
}
Q_EMIT fireRetryCounterPotentiallyChanged();
qCInfo(support) << "Finished PACE for" << pPinId << "with result" << returnCode;
return returnCode;
}
CardReturnCode CardConnectionWorker::destroyPaceChannel()
{
if (!hasCard())
{
return CardReturnCode::CARD_NOT_FOUND;
}
qCInfo(support) << "Destroying PACE channel";
if (mReader->getReaderInfo().isBasicReader())
{
stopSecureMessaging();
MSEBuilder builder(MSEBuilder::P1::ERASE, MSEBuilder::P2::DEFAULT_CHANNEL);
ResponseApdu response;
return mReader->getCard()->transmit(builder.build(), response);
}
else
{
return mReader->getCard()->destroyPaceChannel();
}
}
CardReturnCode CardConnectionWorker::setEidPin(const QString& pNewPin, quint8 pTimeoutSeconds)
{
if (!hasCard())
{
return CardReturnCode::CARD_NOT_FOUND;
}
if (mReader->getReaderInfo().isBasicReader())
{
Q_ASSERT(!pNewPin.isEmpty());
ResetRetryCounterBuilder commandBuilder(pNewPin.toUtf8());
ResponseApdu response;
if (transmit(commandBuilder.build(), response) != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS)
{
qCWarning(card) << "Modify PIN failed";
return CardReturnCode::COMMAND_FAILED;
}
return CardReturnCode::OK;
}
else
{
Q_ASSERT(pNewPin.isEmpty());
return mReader->getCard()->setEidPin(pTimeoutSeconds);
}
}
CardReturnCode CardConnectionWorker::updateRetryCounter()
{
if (!hasCard())
{
return CardReturnCode::CARD_NOT_FOUND;
}
return mReader->updateRetryCounter(sharedFromThis());
}