/*! * CardInfo.cpp * * \brief CardInfo holds smart card information, such as the type and some contained data structure (currently only the EF.CardAccess). * * \copyright Copyright (c) 2014 Governikus GmbH & Co. KG */ #include "asn1/PACEInfo.h" #include "asn1/SecurityInfos.h" #include "CardConnectionWorker.h" #include "CardInfo.h" #include #include #include Q_DECLARE_LOGGING_CATEGORY(card) using namespace governikus; CardInfo::CardInfo(CardType pCardType, QSharedPointer pEfCardAccess, int pRetryCounter, bool pPinDeactivated, bool pPukInoperative) : mCardType(pCardType) , mEfCardAccess(pEfCardAccess) , mRetryCounter(pRetryCounter) , mPinDeactivated(pPinDeactivated) , mPukInoperative(pPukInoperative) { } CardType CardInfo::getCardType() const { return mCardType; } QSharedPointer CardInfo::getEfCardAccess() const { return mEfCardAccess; } QString CardInfo::getEidApplicationPath() const { return mCardType == CardType::EID_CARD ? QStringLiteral("e80704007f00070302") : QString(); } int CardInfo::getRetryCounter() const { return mRetryCounter; } bool CardInfo::isPinDeactivated() const { return mPinDeactivated; } bool CardInfo::isPukInoperative() const { return mPukInoperative; } bool CardInfoFactory::create(const QSharedPointer& pCardConnectionWorker, ReaderInfo& pReaderInfo) { if (pCardConnectionWorker == nullptr) { qCWarning(card) << "No connection to smart card"; pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); return false; } if (!CardInfoFactory::isGermanEidCard(pCardConnectionWorker)) { qCWarning(card) << "Not a German EID card"; pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); return false; } QSharedPointer efCardAccess = readEfCardAccess(pCardConnectionWorker); if (efCardAccess == nullptr || !checkEfCardAccess(efCardAccess)) { qCWarning(card) << "EFCardAccess not found or invalid"; pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); return false; } pReaderInfo.setCardInfo(CardInfo(CardType::EID_CARD, efCardAccess)); pCardConnectionWorker->updateRetryCounter(); return true; } bool CardInfoFactory::isGermanEidCard(const QSharedPointer& pCardConnectionWorker) { // This is actually not specified in the CIF, but we do it to make the PersoSim work // 0. Select the master file CommandApdu command = SelectBuilder(FileRef::masterFile()).build(); ResponseApdu response; CardReturnCode returnCode = pCardConnectionWorker->transmit(command, response); if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { qCWarning(card) << "Cannot select MF"; if (returnCode != CardReturnCode::OK) { qCDebug(card) << "CardConnectionWorker return code" << returnCode; } if (response.getReturnCode() != StatusCode::SUCCESS) { qCDebug(card) << "ResponseApdu return code" << response.getReturnCode(); } return false; } // 1. CL=00, INS=A4=SELECT, P1= 02, P2=0C, Lc=02, Data=2F00 (FI of EF.DIR), Le=absent command = SelectBuilder(FileRef::efDir()).build(); returnCode = pCardConnectionWorker->transmit(command, response); if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { qCWarning(card) << "Cannot select EF.DIR"; return false; } // 2. CL=00, INS=B0=Read Binary, P1=00, P2=00 (no offset), Lc=00, Le=5A command = CommandApdu(QByteArray::fromHex("00B000005A")); returnCode = pCardConnectionWorker->transmit(command, response); if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { qCWarning(card) << "Cannot read EF.DIR"; return false; } // matching value from CIF static const QByteArray matchingValue = QByteArray::fromHex("61324F0FE828BD080FA000000167455349474E500F434941207A752044462E655369676E5100730C4F0AA000000167455349474E61094F07A0000002471001610B4F09E80704007F00070302610C4F0AA000000167455349474E"); bool efDirMatches = response.getData().startsWith(matchingValue); if (!efDirMatches) { qCWarning(card) << "expected EF.DIR(00,5A): " << matchingValue.toHex(); qCWarning(card) << "actual EF.DIR(00,5A): " << response.getData().left(90).toHex(); return false; } return true; } QSharedPointer CardInfoFactory::readEfCardAccess(const QSharedPointer& pCardConnectionWorker) { QByteArray efCardAccessBytes; if (pCardConnectionWorker->readFile(FileRef::efCardAccess(), efCardAccessBytes) != CardReturnCode::OK) { qCCritical(card) << "Error while reading EF.CardAccess: Cannot read EF.CardAccess"; return QSharedPointer(); } auto efCardAccess = EFCardAccess::decode(efCardAccessBytes); if (efCardAccess == nullptr) { qCCritical(card) << "Error while reading EF.CardAccess: Cannot parse EFCardAccess"; } return efCardAccess; } bool CardInfoFactory::checkEfCardAccess(const QSharedPointer& pEfCardAccess) { /* * At least one PACEInfo must have standardized domain parameters */ bool containsStandardizedDomainParameters = false; const auto& infos = pEfCardAccess->getPACEInfos(); for (const auto& paceInfo : infos) { if (paceInfo->isStandardizedDomainParameters()) { containsStandardizedDomainParameters = true; break; } } if (!containsStandardizedDomainParameters) { qCCritical(card) << "Error while reading EF.CardAccess: No PACEInfo with standardized domain parameters found"; return false; } return true; }