VeraCrypt/src/Common/EMVCard.cpp

524 lines
16 KiB
C++

#include "EMVCard.h"
#include "TLVParser.h"
#include "SCardReader.h"
#include "PCSCException.h"
#include "Platform/Finally.h"
#include "Platform/ForEach.h"
#include <vector>
#include <iostream>
#include <algorithm>
#if !defined(TC_WINDOWS) || defined(TC_PROTOTYPE)
#include "Platform/SerializerFactory.h"
#include "Platform/StringConverter.h"
#include "Platform/SystemException.h"
#else
#include "Dictionary.h"
#include "Language.h"
#endif
using namespace std;
namespace VeraCrypt
{
#ifndef TC_WINDOWS
wstring ArrayToHexWideString(const unsigned char * pbData, size_t cbData)
{
static wchar_t* hexChar = L"0123456789ABCDEF";
wstring result;
if (pbData)
{
for (int i = 0; i < cbData; i++)
{
result += hexChar[pbData[i] >> 4];
result += hexChar[pbData[i] & 0x0F];
}
}
return result;
}
#endif
map<EMVCardType, vector<byte>> InitializeSupportedAIDs()
{
map<EMVCardType, vector<byte>> supportedAIDs;
supportedAIDs.insert(std::make_pair(EMVCardType::AMEX, vector<byte>(EMVCard::AMEX_AID, EMVCard::AMEX_AID + (std::end(EMVCard::AMEX_AID) - std::begin(EMVCard::AMEX_AID)))));
supportedAIDs.insert(std::make_pair(EMVCardType::MASTERCARD, vector<byte>(EMVCard::MASTERCARD_AID, EMVCard::MASTERCARD_AID + (std::end(EMVCard::MASTERCARD_AID) - std::begin(EMVCard::MASTERCARD_AID)))));
supportedAIDs.insert(std::make_pair(EMVCardType::VISA, vector<byte>(EMVCard::VISA_AID, EMVCard::VISA_AID + (std::end(EMVCard::VISA_AID) - std::begin(EMVCard::VISA_AID)))));
return supportedAIDs;
}
const byte EMVCard::AMEX_AID[7] = {0xA0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x10};
const byte EMVCard::MASTERCARD_AID[7] = {0xA0, 0x00, 0x00, 0x00, 0x04, 0x10, 0x10};
const byte EMVCard::VISA_AID[7] = {0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10};
const map<EMVCardType, vector<byte>> EMVCard::SUPPORTED_AIDS = InitializeSupportedAIDs();
EMVCard::EMVCard() : SCard(), m_lastPANDigits(L"")
{
}
EMVCard::EMVCard(size_t slotId) : SCard(slotId), m_lastPANDigits(L"")
{
}
EMVCard::~EMVCard()
{
Clear();
}
EMVCard::EMVCard(const EMVCard& other) :
SCard(other),
m_aid(other.m_aid),
m_supportedAids(other.m_supportedAids),
m_iccCert(other.m_iccCert),
m_issuerCert(other.m_issuerCert),
m_cplcData(other.m_cplcData),
m_lastPANDigits(other.m_lastPANDigits)
{
}
EMVCard::EMVCard(EMVCard&& other) :
SCard(other),
m_aid(std::move(other.m_aid)),
m_supportedAids(std::move(other.m_supportedAids)),
m_iccCert(std::move(other.m_iccCert)),
m_issuerCert(std::move(other.m_issuerCert)),
m_cplcData(std::move(other.m_cplcData)),
m_lastPANDigits(std::move(other.m_lastPANDigits))
{
}
EMVCard& EMVCard::operator = (const EMVCard& other)
{
if (this != &other)
{
SCard::operator=(other);
m_aid = other.m_aid;
m_supportedAids = other.m_supportedAids;
m_iccCert = other.m_iccCert;
m_issuerCert = other.m_issuerCert;
m_cplcData = other.m_cplcData;
m_lastPANDigits = other.m_lastPANDigits;
}
return *this;
}
EMVCard& EMVCard::operator = (EMVCard&& other)
{
if (this != &other)
{
SCard::operator=(other);
m_reader = std::move(other.m_reader);
m_aid = std::move(other.m_aid);
m_supportedAids = std::move(other.m_supportedAids);
m_iccCert = std::move(other.m_iccCert);
m_issuerCert = std::move(other.m_issuerCert);
m_cplcData = std::move(other.m_cplcData);
m_lastPANDigits = std::move(other.m_lastPANDigits);
}
return *this;
}
void EMVCard::Clear(void)
{
m_aid.clear();
m_supportedAids.clear();
m_iccCert.clear();
m_issuerCert.clear();
m_cplcData.clear();
m_lastPANDigits.clear();
}
vector<byte> EMVCard::GetCardAID(bool forceContactless)
{
vector<vector<byte>> supportedAIDs;
vector<byte> supportedAIDsPriorities;
vector<pair<byte, vector<byte>>> supportedAIDsSorted;
bool hasBeenReset = false;
CommandAPDU command;
ResponseAPDU response;
vector<byte> responseData;
shared_ptr<TLVNode> rootNode;
shared_ptr<TLVNode> fciNode;
shared_ptr<TLVNode> dfNameNode;
shared_ptr<TLVNode> sfiNode;
shared_ptr<TLVNode> fciIssuerNode;
shared_ptr<TLVNode> fciIssuerDiscretionaryDataNode;
shared_ptr<TLVNode> templateNode;
vector<shared_ptr<TLVNode>> pseDirectoryNodes;
unsigned char sfi;
bool usingContactless = false;
vector<byte> tokenAID;
if (m_aid.size())
return m_aid;
if (m_reader)
{
if (m_reader->IsCardPresent())
{
m_reader->Connect(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, hasBeenReset, true);
m_reader->BeginTransaction();
finally_do_arg (shared_ptr<SCardReader>, m_reader, { finally_arg->EndTransaction(); });
try
{
for (auto it = EMVCard::SUPPORTED_AIDS.begin(); it != EMVCard::SUPPORTED_AIDS.end(); it++)
{
command = CommandAPDU(CLA_ISO7816, INS_SELECT_FILE, 0x04, 0x00, it->second, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR)
{
tokenAID = it->second;
break;
}
}
if (tokenAID.size())
{
m_supportedAids.push_back(tokenAID);
m_aid = tokenAID;
}
else
{
// The following code retrieves the supported AIDs from the card using PSE.
// If the card supports more than one AID, the returned list is sorted using the AIDs priorities,
// the first AID being the one with more priority.
if (forceContactless)
{
usingContactless = true;
command = CommandAPDU(CLA_ISO7816, INS_SELECT_FILE, 0x04, 0x00, EMV_PSE2, 0, sizeof(EMV_PSE2), SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
}
else
{
command = CommandAPDU(CLA_ISO7816, INS_SELECT_FILE, 0x04, 0x00, EMV_PSE1, 0, sizeof(EMV_PSE1), SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() != SW_NO_ERROR)
{
// EMV_PSE2 not found, try EMV_PSE1
usingContactless = true;
command = CommandAPDU(CLA_ISO7816, INS_SELECT_FILE, 0x04, 0x00, EMV_PSE2, 0, sizeof(EMV_PSE2), SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
}
}
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
{
responseData = response.getData();
rootNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
fciNode = TLVParser::TLV_Find(rootNode, EMV_FCI_TAG);
if (fciNode && fciNode->Subs->size() >= 2)
{
if (usingContactless)
{
fciIssuerNode = TLVParser::TLV_Find(fciNode, EMV_FCI_ISSUER_TAG);
if (fciIssuerNode && fciIssuerNode->Subs->size() >= 1)
{
fciIssuerDiscretionaryDataNode = TLVParser::TLV_Find(fciIssuerNode, EMV_FCI_ISSUER_DISCRETIONARY_DATA_TAG);
if (fciIssuerDiscretionaryDataNode && fciIssuerDiscretionaryDataNode->Subs->size() >= 1)
{
for (size_t i = 0; i < fciIssuerDiscretionaryDataNode->Subs->size(); i++)
{
if (fciIssuerDiscretionaryDataNode->Subs->at(i)->Tag == EMV_DIRECTORY_ENTRY_TAG)
{
pseDirectoryNodes.push_back(fciIssuerDiscretionaryDataNode->Subs->at(i));
}
}
}
}
}
else
{
dfNameNode = TLVParser::TLV_Find(fciNode, EMV_DFNAME_TAG);
if (dfNameNode)
{
fciIssuerNode = TLVParser::TLV_Find(fciNode, EMV_FCI_ISSUER_TAG);
if (fciIssuerNode)
{
sfiNode = TLVParser::TLV_Find(fciIssuerNode, EMV_SFI_TAG);
if (sfiNode && sfiNode->Value->size() == 1)
{
sfi = sfiNode->Value->at(0);
byte rec = 1;
do
{
command = CommandAPDU(CLA_ISO7816, INS_READ_RECORD, rec++, (sfi << 3) | 4, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
{
responseData = response.getData();
try
{
templateNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
if (templateNode && templateNode->Tag == EMV_TEMPLATE_TAG && templateNode->Subs->size() >= 1)
{
for (size_t i = 0; i < templateNode->Subs->size(); i++)
{
if (templateNode->Subs->at(i)->Tag == EMV_DIRECTORY_ENTRY_TAG)
{
pseDirectoryNodes.push_back(templateNode->Subs->at(i));
}
}
}
}
catch(TLVException)
{
continue;
}
}
} while (response.getData().size() > 0);
}
}
}
}
}
}
for (size_t i = 0; i < pseDirectoryNodes.size(); i++)
{
shared_ptr<TLVNode> aidNode;
shared_ptr<TLVNode> aidPriorityNode;
aidNode = TLVParser::TLV_Find(pseDirectoryNodes[i], EMV_AID_TAG);
aidPriorityNode = TLVParser::TLV_Find(pseDirectoryNodes[i], EMV_PRIORITY_TAG);
if (aidNode && aidNode->Value->size() > 0 && aidPriorityNode && aidPriorityNode->Value->size() == 1)
{
supportedAIDs.push_back(*aidNode->Value.get());
supportedAIDsPriorities.push_back(aidNode->Value->at(0));
}
}
for(size_t i = 0; i < supportedAIDs.size(); i++)
{
supportedAIDsSorted.push_back(make_pair(supportedAIDsPriorities[i], supportedAIDs[i]));
}
std::sort(supportedAIDsSorted.begin(), supportedAIDsSorted.end());
for(size_t i = 0; i < supportedAIDs.size(); i++)
{
supportedAIDs[i] = supportedAIDsSorted[i].second;
}
if (supportedAIDs.size())
{
m_supportedAids = supportedAIDs;
tokenAID = supportedAIDs[0];
m_aid = tokenAID;
}
}
}
catch (...)
{
}
}
}
return tokenAID;
}
void EMVCard::GetCardContent(vector<byte>& iccCert, vector<byte>& issuerCert, vector<byte>& cplcData)
{
bool hasBeenReset = false;
bool aidSelected = false;
bool iccFound = false;
bool issuerFound = false;
bool cplcFound = false;
vector<byte> emvCardAid;
shared_ptr<TLVNode> rootNode;
shared_ptr<TLVNode> iccPublicKeyCertNode;
shared_ptr<TLVNode> issuerPublicKeyCertNode;
CommandAPDU command;
ResponseAPDU response;
vector<byte> responseData;
iccCert.clear();
issuerCert.clear();
cplcData.clear();
if (m_iccCert.size() && m_issuerCert.size() && m_cplcData.size())
{
iccCert = m_iccCert;
issuerCert = m_issuerCert;
cplcData = m_cplcData;
return;
}
emvCardAid = GetCardAID();
if (emvCardAid.size() == 0)
{
throw EMVUnknownCardType();
}
if (m_reader)
{
if (m_reader->IsCardPresent())
{
m_reader->Connect(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, hasBeenReset, true);
m_reader->BeginTransaction();
finally_do_arg (shared_ptr<SCardReader>, m_reader, { finally_arg->EndTransaction(); });
// First get CPLC before selecting the AID of the card.
command = CommandAPDU(0x80, INS_GET_DATA, (EMV_CPLC_TAG >> 8) & 0xFF, EMV_CPLC_TAG & 0xFF, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
{
cplcFound = true;
cplcData = response.getData();
// Then get the certs.
command = CommandAPDU(CLA_ISO7816, INS_SELECT_FILE, 0x04, 0x00, emvCardAid, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR)
{
aidSelected = true;
// TODO: Send GET PROCESSING OPTIONS to get the AIL and AFL,
// which will then be used to get the actual start and end of sfi and rec.
for (byte sfi = 1; sfi < 32 && (!iccFound || !issuerFound); sfi++)
{
for (byte rec = 1; rec < 17 && (!iccFound || !issuerFound); rec++)
{
command = CommandAPDU(CLA_ISO7816, INS_READ_RECORD, rec, (sfi << 3) | 4, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
{
responseData = response.getData();
try
{
rootNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
}
catch(TLVException)
{
continue;
}
iccPublicKeyCertNode = TLVParser::TLV_Find(rootNode, EMV_ICC_PK_CERT_TAG);
if (iccPublicKeyCertNode && iccPublicKeyCertNode->Value->size() > 0)
{
iccFound = true;
iccCert = *iccPublicKeyCertNode->Value.get();
}
issuerPublicKeyCertNode = TLVParser::TLV_Find(rootNode, EMV_ISS_PK_CERT_TAG);
if (issuerPublicKeyCertNode && issuerPublicKeyCertNode->Value->size() > 0)
{
issuerFound = true;
issuerCert = *issuerPublicKeyCertNode->Value.get();
}
}
}
}
}
}
}
}
if (!cplcFound)
throw EMVCPLCNotFound();
if (!aidSelected)
throw EMVSelectAIDFailed();
if (!iccFound)
throw EMVIccCertNotFound();
if (!issuerFound)
throw EMVIssuerCertNotFound();
m_iccCert = iccCert;
m_issuerCert = issuerCert;
m_cplcData = cplcData;
}
void EMVCard::GetCardPAN(wstring& lastPANDigits)
{
bool hasBeenReset = false;
bool panFound = false;
bool aidSelected = false;
vector<byte> EMVCardAid;
vector<byte> panData;
shared_ptr<TLVNode> rootNode;
shared_ptr<TLVNode> panNode;
CommandAPDU command;
ResponseAPDU response;
vector<byte> responseData;
lastPANDigits = L"";
if (m_lastPANDigits != L"")
{
lastPANDigits = m_lastPANDigits;
return;
}
EMVCardAid = GetCardAID();
if (EMVCardAid.size() == 0)
{
throw EMVUnknownCardType();
}
if (m_reader)
{
if (m_reader->IsCardPresent())
{
m_reader->Connect(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, hasBeenReset, true);
m_reader->BeginTransaction();
finally_do_arg (shared_ptr<SCardReader>, m_reader, { finally_arg->EndTransaction(); });
command = CommandAPDU(CLA_ISO7816, INS_SELECT_FILE, 0x04, 0x00, EMVCardAid, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR)
{
aidSelected = true;
// TODO: Send GET PROCESSING OPTIONS to get the AIL and AFL,
// which will then be used to get the actual start and end of sfi and rec.
for (byte sfi = 1; sfi < 32 && !panFound; sfi++)
{
for (byte rec = 1; rec < 17 && !panFound; rec++)
{
command = CommandAPDU(CLA_ISO7816, INS_READ_RECORD, rec, (sfi << 3) | 4, SCardReader::shortAPDUMaxTransSize);
m_reader->ApduProcessData(command, response);
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
{
responseData = response.getData();
try
{
rootNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
}
catch(TLVException)
{
continue;
}
panNode = TLVParser::TLV_Find(rootNode, EMV_PAN_TAG);
if (panNode && panNode->Value->size() >= 8)
{
panFound = true;
panData = *panNode->Value.get();
panData = vector<byte>(panData.rbegin(), panData.rbegin() + 2); // only interested in last digits
std::swap(panData[0], panData[1]);
lastPANDigits = ArrayToHexWideString(panData.data(), panData.size());
}
}
}
}
}
}
}
if (panData.size())
burn(panData.data(), panData.size());
if (!aidSelected)
throw EMVSelectAIDFailed();
if (!panFound)
throw EMVPANNotFound();
m_lastPANDigits = lastPANDigits;
}
}