diff options
author | Mounir IDRASSI <mounir.idrassi@idrix.fr> | 2023-06-29 00:06:20 +0200 |
---|---|---|
committer | Mounir IDRASSI <mounir.idrassi@idrix.fr> | 2023-06-29 00:06:20 +0200 |
commit | 034b64f4153550cbe5849bcbfc27e187377cc512 (patch) | |
tree | d831496163c3891031765010bf1934406b0c4a3c /src/Common | |
parent | 502ab9112a7624dbd7c1c90c2e12ed45512b8b3c (diff) | |
download | VeraCrypt-034b64f4153550cbe5849bcbfc27e187377cc512.tar.gz VeraCrypt-034b64f4153550cbe5849bcbfc27e187377cc512.zip |
EMV keyfile support: Overall code improvements and bug fixes
Diffstat (limited to 'src/Common')
30 files changed, 3905 insertions, 1320 deletions
diff --git a/src/Common/CommandAPDU.cpp b/src/Common/CommandAPDU.cpp new file mode 100644 index 00000000..b202cb13 --- /dev/null +++ b/src/Common/CommandAPDU.cpp @@ -0,0 +1,366 @@ +#include "CommandAPDU.h" +#include <string.h> + +using namespace std; + +namespace VeraCrypt +{ + CommandAPDU::CommandAPDU() + : m_nc(0), m_ne(0), m_dataOffset(0), m_isExtendedAPDU(false), m_parsingErrorStr(""), m_parsedSuccessfully(false) + { + } + + void CommandAPDU::parse() + { + uint32 l1 = 0; + uint32 l2 = 0; + size_t leOfs = 0; + uint32 l3 = 0; + m_parsingErrorStr = ""; + m_parsedSuccessfully = false; + + if (m_apdu.size() < 4) + { + m_parsingErrorStr = vformat("APDU must be at least 4 bytes long - Length = %zu", m_apdu.size()); + goto failure; + } + + if (m_apdu.size() == 4) + { + // case 1 + goto success; + } + + /*** SHORT APDUs ***/ + l1 = m_apdu[4] & 0xff; + if (m_apdu.size() == 5) + { + // case 2s + m_ne = (l1 == 0) ? 256 : l1; + goto success; + } + if (l1 != 0) + { + if (m_apdu.size() == 4 + 1 + l1) + { + // case 3s + m_nc = l1; + m_dataOffset = 5; + goto success; + } + else if (m_apdu.size() == 4 + 2 + l1) + { + // case 4s + m_nc = l1; + m_dataOffset = 5; + l2 = m_apdu[m_apdu.size() - 1] & 0xff; + m_ne = (l2 == 0) ? 256 : l2; + goto success; + } + else + { + m_parsingErrorStr = vformat("Invalid APDU : b1 = %u, expected length to be %u or %u, got %zu", l1, 4 + 1 + l1, 4 + 2 + l1, m_apdu.size()); + goto failure; + } + } + + if (m_apdu.size() < 7) + { + m_parsingErrorStr = vformat("Invalid APDU : b1 = %u, expected length to be >= 7 , got %zu", l1, m_apdu.size()); + goto failure; + } + + /*** EXTENDED APDUs ***/ + l2 = ((m_apdu[5] & 0xff) << 8) | (m_apdu[6] & 0xff); + if (m_apdu.size() == 7) + { + // case 2e + m_ne = (l2 == 0) ? 65536 : l2; + m_isExtendedAPDU = true; + goto success; + } + if (l2 == 0) + { + m_parsingErrorStr = vformat("Invalid APDU: b1 = %u, b2||b3 = %u, length = %zu", l1, l2, m_apdu.size()); + goto failure; + } + if (m_apdu.size() == 4 + 3 + l2) + { + // case 3e + m_nc = l2; + m_dataOffset = 7; + m_isExtendedAPDU = true; + goto success; + } + if (m_apdu.size() == 4 + 5 + l2) + { + // case 4e + m_nc = l2; + m_dataOffset = 7; + leOfs = m_apdu.size() - 2; + l3 = ((m_apdu[leOfs] & 0xff) << 8) | (m_apdu[leOfs + 1] & 0xff); + m_ne = (l3 == 0) ? 65536 : l3; + m_isExtendedAPDU = true; + goto success; + } + else + { + m_parsingErrorStr = vformat("Invalid APDU : b1 = %u, b2||b3 = %u, expected length to be %u or %u, got %zu", l1, l2, 4 + 3 + l2, 4 + 5 + l2, m_apdu.size()); + goto failure; + } + + success: + m_parsedSuccessfully = true; + + failure: + clear(); + } + + void CommandAPDU::init(byte cla, byte ins, byte p1, byte p2, const byte* data, uint32 dataOffset, uint32 dataLength, uint32 ne) + { + m_nc = 0; + m_ne = 0; + m_dataOffset = 0; + m_isExtendedAPDU = false; + m_parsingErrorStr = ""; + m_parsedSuccessfully = false; + + if (dataLength > 65535) + { + m_parsingErrorStr = vformat("dataLength is too large (> 65535) - dataLength = %u", dataLength); + clear(); + return; + } + if (ne > 65536) + { + m_parsingErrorStr = vformat("ne is too large (> 65536) - ne = %u", ne); + clear(); + return; + } + + m_ne = ne; + m_nc = dataLength; + + if (dataLength == 0) + { + if (m_ne == 0) + { + // case 1 + m_apdu.resize(4, 0); + setHeader(cla, ins, p1, p2); + } + else + { + // case 2s or 2e + if (ne <= 256) + { + // case 2s + // 256 is encoded as 0x00 + byte len = (m_ne != 256) ? (byte)m_ne : 0; + m_apdu.resize(5, 0); + setHeader(cla, ins, p1, p2); + m_apdu[4] = len; + } + else + { + // case 2e + byte l1, l2; + // 65536 is encoded as 0x00 0x00 + if (m_ne == 65536) + { + l1 = 0; + l2 = 0; + } + else + { + l1 = (byte)(m_ne >> 8); + l2 = (byte)m_ne; + } + m_apdu.resize(7, 0); + setHeader(cla, ins, p1, p2); + m_apdu[5] = l1; + m_apdu[6] = l2; + m_isExtendedAPDU = true; + } + } + } + else + { + if (m_ne == 0) + { + // case 3s or 3e + if (dataLength <= 255) + { + // case 3s + m_apdu.resize(4 + 1 + dataLength, 0); + setHeader(cla, ins, p1, p2); + m_apdu[4] = (byte)dataLength; + m_dataOffset = 5; + memcpy(m_apdu.data() + 5, data + dataOffset, dataLength); + } + else + { + // case 3e + m_apdu.resize(4 + 3 + dataLength, 0); + setHeader(cla, ins, p1, p2); + m_apdu[4] = 0; + m_apdu[5] = (byte)(dataLength >> 8); + m_apdu[6] = (byte)dataLength; + m_dataOffset = 7; + memcpy(m_apdu.data() + 7, data + dataOffset, dataLength); + m_isExtendedAPDU = true; + } + } + else + { + // case 4s or 4e + if ((dataLength <= 255) && (m_ne <= 256)) + { + // case 4s + m_apdu.resize(4 + 2 + dataLength, 0); + setHeader(cla, ins, p1, p2); + m_apdu[4] = (byte)dataLength; + m_dataOffset = 5; + memcpy(m_apdu.data() + 5, data + dataOffset, dataLength); + m_apdu[m_apdu.size() - 1] = (m_ne != 256) ? (byte)m_ne : 0; + } + else + { + // case 4e + m_apdu.resize(4 + 5 + dataLength, 0); + setHeader(cla, ins, p1, p2); + m_apdu[4] = 0; + m_apdu[5] = (byte)(dataLength >> 8); + m_apdu[6] = (byte)dataLength; + m_dataOffset = 7; + memcpy(m_apdu.data() + 7, data + dataOffset, dataLength); + if (ne != 65536) + { + size_t leOfs = m_apdu.size() - 2; + m_apdu[leOfs] = (byte)(m_ne >> 8); + m_apdu[leOfs + 1] = (byte)m_ne; + } // else le == 65536: no need to fill in, encoded as 0 + m_isExtendedAPDU = true; + } + } + } + + m_parsedSuccessfully = true; + } + + void CommandAPDU::setHeader(byte cla, byte ins, byte p1, byte p2) + { + m_apdu[0] = (byte)cla; + m_apdu[1] = (byte)ins; + m_apdu[2] = (byte)p1; + m_apdu[3] = (byte)p2; + } + + void CommandAPDU::clear() + { + m_apdu.clear(); + m_nc = 0; + m_ne = 0; + m_dataOffset = 0; + } + + CommandAPDU::CommandAPDU(byte cla, byte ins, byte p1, byte p2, const byte* data, uint32 dataOffset, uint32 dataLength, uint32 ne) + { + init(cla, ins, p1, p2, data, dataOffset, dataLength, ne); + } + + CommandAPDU::CommandAPDU(byte cla, byte ins, byte p1, byte p2) + { + init(cla, ins, p1, p2, NULL, 0, 0, 0); + } + + CommandAPDU::CommandAPDU(byte cla, byte ins, byte p1, byte p2, uint32 ne) + { + init(cla, ins, p1, p2, NULL, 0, 0, ne); + } + + CommandAPDU::CommandAPDU(byte cla, byte ins, byte p1, byte p2, const vector<byte>& data) + { + init(cla, ins, p1, p2, data.data(), 0, (uint32)data.size(), 0); + } + + CommandAPDU::CommandAPDU(byte cla, byte ins, byte p1, byte p2, const byte* data, uint32 dataOffset, uint32 dataLength) + { + init(cla, ins, p1, p2, data, dataOffset, dataLength, 0); + } + + CommandAPDU::CommandAPDU(byte cla, byte ins, byte p1, byte p2, const vector<byte>& data, uint32 ne) + { + init(cla, ins, p1, p2, data.data(), 0, (uint32)data.size(), ne); + } + + CommandAPDU::CommandAPDU(const vector<byte>& apdu) : m_nc(0), m_ne(0), m_dataOffset(0), m_isExtendedAPDU(false) + { + m_apdu = apdu; + parse(); + } + + byte CommandAPDU::getCLA() + { + return m_apdu[0] & 0xff; + } + + byte CommandAPDU::getINS() + { + return m_apdu[1] & 0xff; + } + + byte CommandAPDU::getP1() + { + return m_apdu[2] & 0xff; + } + + byte CommandAPDU::getP2() + { + return m_apdu[3] & 0xff; + } + + uint32 CommandAPDU::getNc() + { + return m_nc; + } + + const vector<byte> CommandAPDU::getData() + { + vector<byte> data; + + if (m_nc > 0) + { + data.resize(m_nc, 0); + memcpy(data.data(), m_apdu.data() + m_dataOffset, data.size()); + } + + return data; + } + + uint32 CommandAPDU::getNe() + { + return m_ne; + } + + const vector<byte> CommandAPDU::getAPDU() + { + return m_apdu; + } + + bool CommandAPDU::isExtended() + { + return m_isExtendedAPDU; + } + + bool CommandAPDU::isValid() + { + return m_parsedSuccessfully; + } + + std::string CommandAPDU::getErrorStr() + { + return m_parsingErrorStr; + } +} + diff --git a/src/Common/CommandAPDU.h b/src/Common/CommandAPDU.h new file mode 100644 index 00000000..98825faa --- /dev/null +++ b/src/Common/CommandAPDU.h @@ -0,0 +1,93 @@ +#ifndef TC_HEADER_Common_CommandAPDU +#define TC_HEADER_Common_CommandAPDU + +#include "Platform/PlatformBase.h" +#include <stdarg.h> + +namespace VeraCrypt +{ + inline const std::string vformat(const char* zcFormat, ...) + { + if (zcFormat) + { + va_list vaArgs; + va_start(vaArgs, zcFormat); + + const int iLen = vsnprintf(NULL, 0, zcFormat, vaArgs); + va_end(vaArgs); + + if (iLen) + { + std::vector<char> zc((size_t)iLen + 1); + va_start(vaArgs, zcFormat); + vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs); + va_end(vaArgs); + + return std::string(zc.data(), iLen); + } + } + + return ""; + } + + class CommandAPDU + { + protected: + + vector<byte> m_apdu; + uint32 m_nc; + uint32 m_ne; + uint32 m_dataOffset; + bool m_isExtendedAPDU; + std::string m_parsingErrorStr; + bool m_parsedSuccessfully; + + void parse(); + void init(byte cla, byte ins, byte p1, byte p2, const byte* data, uint32 dataOffset, uint32 dataLength, uint32 ne); + void setHeader(byte cla, byte ins, byte p1, byte p2); + + public: + + void clear(); + + CommandAPDU(); + + CommandAPDU(byte cla, byte ins, byte p1, byte p2, const byte* data, uint32 dataOffset, uint32 dataLength, uint32 ne); + + CommandAPDU(byte cla, byte ins, byte p1, byte p2); + + CommandAPDU(byte cla, byte ins, byte p1, byte p2, uint32 ne); + + CommandAPDU(byte cla, byte ins, byte p1, byte p2, const vector<byte>& data); + + CommandAPDU(byte cla, byte ins, byte p1, byte p2, const byte* data, uint32 dataOffset, uint32 dataLength); + + CommandAPDU(byte cla, byte ins, byte p1, byte p2, const vector<byte>& data, uint32 ne); + + CommandAPDU(const vector<byte>& apdu); + + byte getCLA(); + + byte getINS(); + + byte getP1(); + + byte getP2(); + + uint32 getNc(); + + const vector<byte> getData(); + + uint32 getNe(); + + const vector<byte> getAPDU(); + + bool isValid(); + + std::string getErrorStr(); + + bool isExtended(); + }; +}; + +#endif // TC_HEADER_Common_CommandAPDU
\ No newline at end of file diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index dfcc3d03..6933eb71 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -207,7 +207,7 @@ BOOL LastMountedVolumeDirty; BOOL MountVolumesAsSystemFavorite = FALSE; BOOL FavoriteMountOnArrivalInProgress = FALSE; BOOL MultipleMountOperationInProgress = FALSE; -BOOL ActivateEMVOption = FALSE; +BOOL EMVSupportEnabled = FALSE; volatile BOOL NeedPeriodicDeviceListUpdate = FALSE; BOOL DisablePeriodicDeviceListUpdate = FALSE; @@ -12373,7 +12373,7 @@ BOOL CALLBACK SecurityTokenKeyfileDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam WaitCursor(); finally_do ({ NormalCursor(); }); - keyfiles = Token::GetAvailableKeyfiles(ActivateEMVOption); + keyfiles = Token::GetAvailableKeyfiles(EMVSupportEnabled); } catch (UserAbort&) { @@ -12415,10 +12415,14 @@ BOOL CALLBACK SecurityTokenKeyfileDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam { BOOL selected = (ListView_GetNextItem (GetDlgItem (hwndDlg, IDC_TOKEN_FILE_LIST), -1, LVIS_SELECTED) != -1); BOOL deletable = selected; + // Multiple key files can be selected. + // Therefore, if one of them is not deletable, it means the delete button must be disabled for all. foreach (const shared_ptr<TokenKeyfile> &keyfile, SecurityTokenKeyfileDlgGetSelected (hwndDlg, keyfiles)) { - if( ! keyfile->Token->isEditable()){ + if (!keyfile->Token->isEditable()) + { deletable = false; + break; } } EnableWindow (GetDlgItem (hwndDlg, IDC_EXPORT), selected); @@ -12469,7 +12473,7 @@ BOOL CALLBACK SecurityTokenKeyfileDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam SecurityToken::CreateKeyfile (newParams.SlotId, keyfileDataVector, newParams.Name); - keyfiles = Token::GetAvailableKeyfiles(ActivateEMVOption); + keyfiles = Token::GetAvailableKeyfiles(EMVSupportEnabled); SecurityTokenKeyfileDlgFillList (hwndDlg, keyfiles); } catch (Exception &e) @@ -12551,7 +12555,7 @@ BOOL CALLBACK SecurityTokenKeyfileDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam SecurityToken::DeleteKeyfile (dynamic_cast<SecurityTokenKeyfile&>(*keyfile.get())); } - keyfiles = Token::GetAvailableKeyfiles(ActivateEMVOption); + keyfiles = Token::GetAvailableKeyfiles(EMVSupportEnabled); SecurityTokenKeyfileDlgFillList (hwndDlg, keyfiles); } catch (Exception &e) diff --git a/src/Common/Dlgcode.h b/src/Common/Dlgcode.h index 2004eb2a..7f7efb0f 100644 --- a/src/Common/Dlgcode.h +++ b/src/Common/Dlgcode.h @@ -166,7 +166,7 @@ extern BOOL LastMountedVolumeDirty; extern BOOL MountVolumesAsSystemFavorite; extern BOOL FavoriteMountOnArrivalInProgress; extern BOOL MultipleMountOperationInProgress; -extern BOOL ActivateEMVOption; +extern BOOL EMVSupportEnabled; extern volatile BOOL NeedPeriodicDeviceListUpdate; extern BOOL DisablePeriodicDeviceListUpdate; diff --git a/src/Common/EMVCard.cpp b/src/Common/EMVCard.cpp new file mode 100644 index 00000000..72c0952b --- /dev/null +++ b/src/Common/EMVCard.cpp @@ -0,0 +1,523 @@ +#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; + } +} diff --git a/src/Common/EMVCard.h b/src/Common/EMVCard.h new file mode 100644 index 00000000..116de705 --- /dev/null +++ b/src/Common/EMVCard.h @@ -0,0 +1,69 @@ +#ifndef TC_HEADER_Common_EMVCard +#define TC_HEADER_Common_EMVCard + +#include "Platform/PlatformBase.h" +#if defined (TC_WINDOWS) && !defined (TC_PROTOTYPE) +# include "Exception.h" +#else +# include "Platform/Exception.h" +#endif + +#include "Token.h" +#include "SCard.h" + +namespace VeraCrypt +{ + typedef enum EMVCardType + { + NONE = 0, + AMEX, + MASTERCARD, + VISA + } EMVCardType; + + class EMVCard : public SCard + { + protected: + + // The following fields will only be empty if the card has not been read yet. + // After the card has been read, and if some or all fields cannot be read, the EMVCard + // object will be considered invalid and will not be included in the list of available cards + // of EMVToken. + vector<byte> m_aid; + vector<vector<byte>> m_supportedAids; + vector<byte> m_iccCert; + vector<byte> m_issuerCert; + vector<byte> m_cplcData; + wstring m_lastPANDigits; + + public: + + // Add other AIDS + // https://gist.github.com/pvieito/6224eed92c99b069f6401996c548d0e4 + // https://ambimat.com/developer-resources/list-of-application-identifiers-aid/ + const static byte AMEX_AID[7]; + const static byte MASTERCARD_AID[7]; + const static byte VISA_AID[7]; + const static map<EMVCardType, vector<byte>> SUPPORTED_AIDS; + + EMVCard(); + EMVCard(size_t slotId); + EMVCard(const EMVCard& other); + EMVCard(EMVCard&& other); + EMVCard& operator = (const EMVCard& other); + EMVCard& operator = (EMVCard&& other); + virtual ~EMVCard(); + + void Clear(void); + + // Retrieves the card's AID. + // It first checks the card against a list of supported AIDs. + // If that fails, it tries getting the AID from the card using PSE + vector<byte> GetCardAID(bool forceContactless = false); + + void GetCardContent(vector<byte>& iccCert, vector<byte>& issuerCert, vector<byte>& cplcData); + void GetCardPAN(wstring& lastPANDigits); + }; +} + +#endif // TC_HEADER_Common_EMVCard diff --git a/src/Common/EMVToken.cpp b/src/Common/EMVToken.cpp index aac59a6a..0fe3dc1f 100644 --- a/src/Common/EMVToken.cpp +++ b/src/Common/EMVToken.cpp @@ -1,11 +1,13 @@ #include "EMVToken.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" @@ -16,29 +18,52 @@ #include "Language.h" #endif - using namespace std; namespace VeraCrypt { + void AppendData(vector<byte>& buffer, const unsigned char* pbData, size_t cbData, size_t from, size_t length, bool bEncodeLength = false) + { + if (cbData > 0 && from <= cbData - 2 && length > 0 && length <= cbData - from) + { + size_t offset = (bEncodeLength ? 4 : 0); + size_t orgSize = buffer.size(); + buffer.resize(orgSize + length + offset); + if (bEncodeLength) + { + unsigned int dwLength = (unsigned int)(length); + memcpy(buffer.data() + orgSize, &dwLength, 4); + } + memcpy(buffer.data() + orgSize + offset, pbData + from, length); + } + } + + /* ****************************************************************************************************************************************** */ - IccDataExtractor EMVToken::extractor; + map <unsigned long int, shared_ptr<EMVCard>> EMVToken::EMVCards; EMVTokenInfo::~EMVTokenInfo() { - burn(&Label,Label.size()); + if (Label.size() > 0) + burn(&Label[0], Label.size() * sizeof(wchar_t)); } - EMVTokenKeyfile::EMVTokenKeyfile(const TokenKeyfilePath& path) + EMVTokenKeyfile::EMVTokenKeyfile() { Id = EMV_CARDS_LABEL; Token = shared_ptr<EMVTokenInfo>(new EMVTokenInfo()); + } + + EMVTokenKeyfile::EMVTokenKeyfile(const TokenKeyfilePath& path) + { wstring pathStr = path; unsigned long slotId; if (swscanf(pathStr.c_str(), TC_EMV_TOKEN_KEYFILE_URL_PREFIX TC_EMV_TOKEN_KEYFILE_URL_SLOT L"/%lu", &slotId) != 1) throw InvalidEMVPath(); + Id = EMV_CARDS_LABEL; + Token = shared_ptr<EMVTokenInfo>(new EMVTokenInfo()); Token->SlotId = slotId; } @@ -51,12 +76,46 @@ namespace VeraCrypt void EMVTokenKeyfile::GetKeyfileData(vector <byte>& keyfileData) const { - #ifdef TC_WINDOWS - EMVToken::extractor.InitLibrary(); - #endif + map <unsigned long int, shared_ptr<EMVCard>>::iterator emvCardsIt; + shared_ptr<EMVCard> card; + vector<byte> iccCert; + vector<byte> issuerCert; + vector<byte> cplcData; + bool addNewCard = true; + + keyfileData.clear(); + + emvCardsIt = EMVToken::EMVCards.find(Token->SlotId); + if (emvCardsIt != EMVToken::EMVCards.end()) + { + // An EMVCard object has already been created for this slotId. + // We check that it's SCard handle is still valid. + // If it is, we use the existing EMVCard to get the card's content. + // If it is not, we remove the EMVCard from EMVCards and create a new one. + + if (emvCardsIt->second->IsCardHandleValid()) + { + emvCardsIt->second->GetCardContent(iccCert, issuerCert, cplcData); + addNewCard = false; + } + else + { + EMVToken::EMVCards.erase(emvCardsIt); + } + } - EMVToken::extractor.GetReaders(); - EMVToken::extractor.GettingAllCerts(Token->SlotId, keyfileData); + if (addNewCard) + { + // An EMVCard object does not exist for this slotId, or exists but its handle is not valid anymore. + // We create a new one and then add it to EMVCards. + card = make_shared<EMVCard>(Token->SlotId); + card->GetCardContent(iccCert, issuerCert, cplcData); + EMVToken::EMVCards.insert(make_pair(Token->SlotId, card)); + } + + AppendData(keyfileData, iccCert.data(), iccCert.size(), 0, iccCert.size()); + AppendData(keyfileData, issuerCert.data(), issuerCert.size(), 0, issuerCert.size()); + AppendData(keyfileData, cplcData.data(), cplcData.size(), 0, cplcData.size()); } bool EMVToken::IsKeyfilePathValid(const wstring& emvTokenKeyfilePath) @@ -64,36 +123,52 @@ namespace VeraCrypt return emvTokenKeyfilePath.find(TC_EMV_TOKEN_KEYFILE_URL_PREFIX) == 0; } - vector<EMVTokenKeyfile> EMVToken::GetAvailableKeyfiles(unsigned long int* slotIdFilter, const wstring keyfileIdFilter) { - #ifdef TC_WINDOWS - EMVToken::extractor.InitLibrary(); - #endif - + vector<EMVTokenKeyfile> EMVToken::GetAvailableKeyfiles(unsigned long int* slotIdFilter, const wstring& keyfileIdFilter) + { vector <EMVTokenKeyfile> keyfiles; - unsigned long int nb = 0; - - nb = EMVToken::extractor.GetReaders(); - + vector<wstring> readers; - for(unsigned long int slotId = 0; slotId<nb; slotId++) + readers = EMVCard::manager.GetReaders(); + for (unsigned long int slotId = 0; slotId < readers.size(); slotId++) { EMVTokenInfo token; if (slotIdFilter && *slotIdFilter != slotId) continue; - try{ + try + { token = GetTokenInfo(slotId); - } catch(EMVUnknownCardType) { + } + catch(ParameterIncorrect&) + { + continue; + } + catch(EMVUnknownCardType&) + { + continue; + } + catch(EMVSelectAIDFailed&) + { + continue; + } + catch(EMVPANNotFound&) + { + continue; + } + catch(PCSCException&) + { continue; - }catch(PCSCException){ - - continue; } EMVTokenKeyfile keyfile; - keyfile.Token->SlotId = slotId; keyfile.Token = shared_ptr<TokenInfo>(new EMVTokenInfo(token)); + keyfile.Token->SlotId = slotId; + + // keyfileIdFilter is of no use for EMV tokens as the Id is always set to EMV_CARDS_LABEL. + // Nonetheless, we keep the following code that is also used in SecurityToken::GetAvailableKeyfiles. + if (keyfile.Id.empty() || (!keyfileIdFilter.empty() && keyfileIdFilter != keyfile.Id)) + continue; keyfiles.push_back(keyfile); @@ -102,20 +177,49 @@ namespace VeraCrypt } return keyfiles; - } - - EMVTokenInfo EMVToken::GetTokenInfo(unsigned long int slotId) { + EMVTokenInfo EMVToken::GetTokenInfo(unsigned long int slotId) + { EMVTokenInfo token; + wstring lastPANDigits; + map <unsigned long int, shared_ptr<EMVCard>>::iterator emvCardsIt; + shared_ptr<EMVCard> card; + bool addNewCard = true; + + emvCardsIt = EMVCards.find(slotId); + if (emvCardsIt != EMVCards.end()) + { + // An EMVCard object has already been created for this slotId. + // We check that it's SCard handle is still valid. + // If it is, we use the existing EMVCard to get the card's PAN. + // If it is not, we remove the EMVCard from EMVCards and create a new one. + + if (emvCardsIt->second->IsCardHandleValid()) + { + emvCardsIt->second->GetCardPAN(lastPANDigits); + addNewCard = false; + } + else + { + EMVCards.erase(emvCardsIt); + } + } + + if (addNewCard) + { + // An EMVCard object does not exist for this slotId, or exists but its handle is not valid anymore. + // We create a new one and then add it to EMVCards. + card = make_shared<EMVCard>(slotId); + card->GetCardPAN(lastPANDigits); + EMVCards.insert(make_pair(slotId, card)); + } + token.SlotId = slotId; - //card numbers extraction - std::string pan; - EMVToken::extractor.GettingPAN(slotId, pan); token.Label = L"EMV card **** "; - token.Label += wstring (pan.begin(), pan.end()); - burn(&pan[0],pan.size()); + token.Label += lastPANDigits; + burn(&lastPANDigits[0], lastPANDigits.size() * sizeof(wchar_t)); + return token; } - } diff --git a/src/Common/EMVToken.h b/src/Common/EMVToken.h index 1d84ec28..a1c0a2e1 100644 --- a/src/Common/EMVToken.h +++ b/src/Common/EMVToken.h @@ -6,44 +6,36 @@ #define EMV_CARDS_LABEL L"EMV Certificates" -#include "Platform/PlatformBase.h" -#if defined (TC_WINDOWS) && !defined (TC_PROTOTYPE) -# include "Exception.h" -#else -# include "Platform/Exception.h" -#endif - -#include "Token.h" -#include "IccDataExtractor.h" - -namespace VeraCrypt { +#include "EMVCard.h" +namespace VeraCrypt +{ struct EMVTokenInfo: TokenInfo { virtual ~EMVTokenInfo(); - virtual BOOL isEditable() const {return false;} + virtual BOOL isEditable() const { return false; } }; struct EMVTokenKeyfile: TokenKeyfile { - EMVTokenKeyfile(){Id = EMV_CARDS_LABEL; Token = shared_ptr<EMVTokenInfo>(new EMVTokenInfo());}; + EMVTokenKeyfile(); EMVTokenKeyfile(const TokenKeyfilePath& path); + virtual ~EMVTokenKeyfile() {}; virtual operator TokenKeyfilePath () const; virtual void GetKeyfileData(vector <byte>& keyfileData) const; - }; - class EMVToken { - private: - static IccDataExtractor extractor; + class EMVToken + { public: static bool IsKeyfilePathValid(const wstring& emvTokenKeyfilePath); - static vector<EMVTokenKeyfile> GetAvailableKeyfiles(unsigned long int* slotIdFilter = nullptr, const wstring keyfileIdFilter = wstring()); + static vector<EMVTokenKeyfile> GetAvailableKeyfiles(unsigned long int* slotIdFilter = nullptr, const wstring& keyfileIdFilter = wstring()); static EMVTokenInfo GetTokenInfo(unsigned long int slotId); friend void EMVTokenKeyfile::GetKeyfileData(vector <byte>& keyfileData) const; + static map <unsigned long int, shared_ptr<EMVCard>> EMVCards; }; } diff --git a/src/Common/IccDataExtractor.cpp b/src/Common/IccDataExtractor.cpp index d98bc360..e69de29b 100644 --- a/src/Common/IccDataExtractor.cpp +++ b/src/Common/IccDataExtractor.cpp @@ -1,777 +0,0 @@ -// -// Created by bshp on 1/14/23. -// - -#include "IccDataExtractor.h" - -#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 - -#include "Tcdefs.h" - -namespace VeraCrypt -{ - - - #ifdef TC_WINDOWS - bool VeraCrypt::IccDataExtractor::Initialized; - #endif - //using namespace std; - const BYTE IccDataExtractor::SELECT_MASTERCARD[] = {00, 0xA4, 0x04, 00, 0x07, 0xA0, 00, 00, 00, 0x04, 0x10, 0x10}; - const BYTE IccDataExtractor::SELECT_VISA[] = {00, 0xA4, 0x04, 00, 0x07, 0xA0, 00, 00, 00, 0x03, 0x10, 0x10}; - const BYTE IccDataExtractor::SELECT_AMEX[] = {00, 0xA4, 0x04, 00, 0x07, 0xA0, 00, 00, 00, 00, 0x25, 0x10}; - const BYTE * IccDataExtractor::SELECT_TYPES[]={SELECT_MASTERCARD, SELECT_VISA, SELECT_AMEX}; - - IccDataExtractor::IccDataExtractor(){} - - IccDataExtractor::~IccDataExtractor(){ - /* Disconnect card if connected */ - if(hCard){ - #ifdef TC_WINDOWS - WSCardDisconnect(hContext,hCard); - #else - SCardDisconnect(hContext,hCard); - #endif - } - /* Release memory that has been returned from the resource manager using the SCARD_AUTOALLOCATE length - * designator*/ - if (mszReaders){ - #ifdef TC_WINDOWS - WSCardFreeMemory(hContext, mszReaders); - #else - SCardFreeMemory(hContext, mszReaders); - #endif - } - - /* Closing the established resource manager context freeing any resources allocated under that context - * including SCARDHANDLE objects and memory allocated using the SCARD_AUTOALLOCATE length designator*/ - if(hContext){ - #ifdef TC_WINDOWS - WSCardReleaseContext(hContext); - #else - SCardReleaseContext(hContext); - #endif - } - - /* Freeing winscard library */ - #ifdef TC_WINDOWS - FreeLibrary(WinscardLibraryHandle); - #endif - } - - #ifdef TC_WINDOWS - void IccDataExtractor::InitLibrary(){ - - if(Initialized) return; - - /* Getting the System32 directory */ - char sysDir[MAX_PATH-20]; - GetSystemDirectoryA(sysDir, MAX_PATH); - - /* Getting the winscard dll path directory */ - char winscardPath[MAX_PATH]; - sprintf_s(winscardPath, "%s\\Winscard.dll", sysDir); - - /* Loading the winscard dll from System32 */ - WinscardLibraryHandle = LoadLibraryA(winscardPath); - throw_sys_if(!WinscardLibraryHandle); - - /* Fetching the functions pointers from the dll */ - WSCardEstablishContext = (SCardEstablishContextPtr) GetProcAddress(WinscardLibraryHandle,"SCardEstablishContext"); - if(!WSCardEstablishContext) throw WinscardLibraryNotInitialized(); - - WSCardReleaseContext= (SCardReleaseContextPtr) GetProcAddress(WinscardLibraryHandle,"SCardReleaseContext"); - if(!WSCardReleaseContext) throw WinscardLibraryNotInitialized(); - - WSCardConnectA = (SCardConnectAPtr) GetProcAddress(WinscardLibraryHandle,"SCardConnectA"); - if(!WSCardConnectA) throw WinscardLibraryNotInitialized(); - - WSCardDisconnect = (SCardDisconnectPtr) GetProcAddress(WinscardLibraryHandle,"SCardDisconnect"); - if(!WSCardDisconnect) throw WinscardLibraryNotInitialized(); - - WSCardFreeMemory = ( SCardFreeMemoryPtr) GetProcAddress(WinscardLibraryHandle,"SCardFreeMemory"); - if(!WSCardFreeMemory) throw WinscardLibraryNotInitialized(); - - WSCardListReadersA = (SCardListReadersAPtr) GetProcAddress(WinscardLibraryHandle,"SCardListReadersA"); - if(!WSCardListReadersA) throw WinscardLibraryNotInitialized(); - - WSCardTransmit = ( SCardTransmitPtr) GetProcAddress(WinscardLibraryHandle,"SCardTransmit"); - if(!WSCardTransmit) throw WinscardLibraryNotInitialized(); - - Initialized = true; - - } - #endif - - /* Establishing the resource manager context (the scope) within which database operations are performed. - * The module of the smart card subsystem that manages access to multiple readers and smart cards. The - * resource manager identifies and tracks resources, allocates readers and resources across multiple - * applications,and supports transaction primitives for accessing services available on a given card.*/ - int IccDataExtractor::EstablishRSContext(){ - - #ifdef TC_WINDOWS - LONG returnValue = WSCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); - #else - LONG returnValue = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); - #endif - - /* Check if the establishment of the context was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - return EXIT_SUCCESS; - } - - /* Detecting available readers and filling the reader table */ - unsigned long IccDataExtractor::GetReaders(){ - - #ifdef TC_WINDOWS - if(!Initialized) - throw WinscardLibraryNotInitialized(); - #endif - - EstablishRSContext(); - - /* Length of the mszReaders buffer in characters. If the buffer length is specified as - * SCARD_AUTOALLOCATE, then mszReaders is converted to a pointer to a byte pointer, and - * receives the address of a block of memory containing the multi-string structure */ - DWORD dwReaders = SCARD_AUTOALLOCATE; - - /* Retrieving the available readers list and putting it in mszReaders*/ // Use LPSTR on linux - #ifdef TC_WINDOWS - LONG returnValue = WSCardListReadersA(hContext, NULL, (LPSTR)&mszReaders, &dwReaders); - #else - LONG returnValue = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders); - #endif - - /* If the is no readers, return */ - if(returnValue == SCARD_E_NO_READERS_AVAILABLE) return 0; - - /* Check if the listing of the connected readers was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - nbReaders = 0; - LPSTR ReaderPtr = mszReaders; - - /* Getting the total number of readers */ - while (*ReaderPtr != '\0') - { - readers.push_back(ReaderPtr); - ReaderPtr += strlen((char*)ReaderPtr) + 1; - nbReaders++; - } - - return nbReaders; - } - - /* Connecting to the card in the given reader*/ - int IccDataExtractor::ConnectCard(unsigned long int reader_nb){ - - /* Check if the given reader slot number is possible */ - if (reader_nb < 0 || reader_nb >= nbReaders) - throw InvalidEMVPath(); - - dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED; - - #ifdef TC_WINDOWS - LONG returnValue = WSCardConnectA(hContext, readers[reader_nb], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); - #else - LONG returnValue = SCardConnect(hContext, readers[reader_nb], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); - #endif - - /* Check is the card connection was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - return EXIT_SUCCESS; - } - - /* Disconnect the card currently connected*/ - int IccDataExtractor::DisconnectCard(){ - #ifdef TC_WINDOWS - LONG returnValue = WSCardDisconnect(hCard, SCARD_UNPOWER_CARD); - #else - LONG returnValue = SCardDisconnect(hCard, SCARD_UNPOWER_CARD); - #endif - - /* Check is the card deconnection was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - return EXIT_SUCCESS; - } - - /* Testing if the card contains the application of the given EMV type (0:Mastercard, 1:Visa, 2:Amex) */ - bool IccDataExtractor::TestingCardType(const int SELECT_TYPE_NUMBER){ - - const BYTE * SELECTED_TYPE = SELECT_TYPES[SELECT_TYPE_NUMBER]; - - BYTE pbRecvBuffer[64]; /* Buffer to receive the card response */ - - DWORD dwSendLength = SELECT_TYPE_SIZE; /* Set the size of the send buffer */ - DWORD dwRecvLength = sizeof(pbRecvBuffer); /* Set the size of the reception buffer */ - - /* Set up the io request */ - SCARD_IO_REQUEST ioRequest; - ioRequest.dwProtocol = dwActiveProtocol; - ioRequest.cbPciLength = sizeof(ioRequest); - - #ifdef TC_WINDOWS - LONG returnValue = WSCardTransmit(hCard, &ioRequest, SELECTED_TYPE, dwSendLength, NULL, pbRecvBuffer, &dwRecvLength); - #else - LONG returnValue = SCardTransmit(hCard, &ioRequest, SELECTED_TYPE, dwSendLength, NULL, pbRecvBuffer, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* It received a response. Check if it didn't get a recognisable response */ - if (dwRecvLength < 2) - return false; - - /* Check if the command successfully executed (the card is the type passed in the parameter) */ - if (pbRecvBuffer[0] == 0x61) - return true; - - return false; - } - - /* Getting the ICC Public Key Certificates and the Issuer Public Key Certificates by parsing the application - * (!NEED TO TEST CARD TYPE TO SELECT APPLICATION FIRST!)*/ - void IccDataExtractor::GetCerts(vector<byte> &CERTS){ - - CERTS.clear(); - - bool iccFound= false; - bool issuerFound= false; - - shared_ptr<TLVNode> node; - shared_ptr<TLVNode> ICC_Public_Key_Certificate; - shared_ptr<TLVNode> Issuer_PK_Certificate; - - BYTE pbRecvBuffer[64]; /* Buffer to receive the card response */ - BYTE pbRecvBufferFat[256]; /* Bigger buffer to receive the card response */ - - DWORD dwSendLength; /* Size of the send buffer */ - DWORD dwRecvLength; /* Size of the reception buffer */ - - /* Set up the io request */ - SCARD_IO_REQUEST ioRequest; - ioRequest.dwProtocol = dwActiveProtocol; - ioRequest.cbPciLength = sizeof(ioRequest); - - LONG returnValue; - - /* Parsing root folders */ - for (int sfi = 0; sfi < 32; sfi++) - { - /* Parsing sub folders */ - for (int rec = 0; rec < 17; rec++) - { - BYTE SELECT_APDU_FILE[] = {00, 0xB2, static_cast<unsigned char>(rec), static_cast<unsigned char>((sfi << 3) | 4), 0x00}; - - dwSendLength = sizeof(SELECT_APDU_FILE); - dwRecvLength = sizeof(pbRecvBuffer); - - /* Check if there is data in the folder */ - #ifdef TC_WINDOWS - returnValue = WSCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength,NULL, pbRecvBuffer, &dwRecvLength); - #else - returnValue = SCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength,NULL, pbRecvBuffer, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* There is no data in the folder */ - if (pbRecvBuffer[0] != 0x6C) - continue; - - /* It set the proper expected length of the data in the APDU */ - SELECT_APDU_FILE[4] = pbRecvBuffer[1]; - - dwRecvLength = sizeof(pbRecvBufferFat); - - /* Get the data from the folder */ - #ifdef TC_WINDOWS - returnValue = WSCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength, NULL, pbRecvBufferFat, &dwRecvLength); - #else - returnValue = SCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength, NULL, pbRecvBufferFat, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* It received a response. Check if it didn't get a recognisable response */ - if (dwRecvLength < 2) - continue; - - /* Parsing the TLV */ - try{ - node = TLVParser::TLV_Parse(pbRecvBufferFat,sizeof(pbRecvBufferFat)); - }catch(TLVException){ - continue; - } - - /* Finding the ICC_Public_Key_Certificate */ - try{ - ICC_Public_Key_Certificate = TLVParser::TLV_Find(node, 0x9F46); - }catch(TLVException){ - continue; - } - if(ICC_Public_Key_Certificate) { - iccFound=true; - for (int i = 0; i < ICC_Public_Key_Certificate->Length;i++) { - CERTS.push_back(static_cast<byte>(ICC_Public_Key_Certificate->Value[i])); - } - } - - /* Finding the Issuer_Public_Key_Certificate */ - try{ - Issuer_PK_Certificate = TLVParser::TLV_Find(node, 0x90); - }catch(TLVException){ - continue; - } - - if(Issuer_PK_Certificate) { - issuerFound=true; - for (int i = 0; i < Issuer_PK_Certificate->Length;i++) { - CERTS.push_back(static_cast<byte>(Issuer_PK_Certificate->Value[i])); - } - } - - /* Limiting the search to at least one occurrence of both PKs to speed up the process. - * There might be more certificates tho */ - if(iccFound && issuerFound){ - burn(pbRecvBuffer, sizeof(pbRecvBuffer)); - burn(pbRecvBufferFat, sizeof(pbRecvBufferFat)); - return; - } - } - } - burn(pbRecvBuffer, sizeof(pbRecvBuffer)); - burn(pbRecvBufferFat, sizeof(pbRecvBufferFat)); - throw EMVKeyfileDataNotFound(); - } - - /* Getting CPCL data from the card*/ - void IccDataExtractor::GetCPCL(vector<byte> &v){ - - BYTE SELECT_APDU_CPCL[] = {0x80,0xCA, 0x9F, 0x7F, 0x00}; - - BYTE pbRecvBuffer[64]; /* Buffer to receive the card response */ - BYTE pbRecvBufferFat[256]; /* Bigger buffer to receive the card response */ - - DWORD dwSendLength = sizeof (SELECT_APDU_CPCL); /* Set the size of the send buffer */ - DWORD dwRecvLength = sizeof(pbRecvBuffer); /* Set the size of the reception buffer */ - - /* Set up the io request */ - SCARD_IO_REQUEST ioRequest; - ioRequest.dwProtocol = dwActiveProtocol; - ioRequest.cbPciLength = sizeof(ioRequest); - - /* Check if there is the TAG for CPCL Data in the card */ - #ifdef TC_WINDOWS - LONG returnValue = WSCardTransmit(hCard, &ioRequest, SELECT_APDU_CPCL, dwSendLength, NULL, pbRecvBuffer, &dwRecvLength); - #else - LONG returnValue = SCardTransmit(hCard, &ioRequest, SELECT_APDU_CPCL, dwSendLength, NULL, pbRecvBuffer, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* Not the correct APDU response code */ - if (pbRecvBuffer[0] != 0x6C) - throw EMVKeyfileDataNotFound(); - - /* It set the proper expected length of the data in the APDU */ - SELECT_APDU_CPCL[4] = pbRecvBuffer[1]; - - dwRecvLength = sizeof(pbRecvBufferFat); - - /* Get the CPCL data */ - #ifdef TC_WINDOWS - returnValue = WSCardTransmit(hCard, &ioRequest, SELECT_APDU_CPCL, dwSendLength,NULL, pbRecvBufferFat, &dwRecvLength); - #else - returnValue = SCardTransmit(hCard, &ioRequest, SELECT_APDU_CPCL, dwSendLength,NULL, pbRecvBufferFat, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* It received a response. Check if it didn't get a recognisable response */ - if (dwRecvLength < 2) - throw EMVKeyfileDataNotFound(); - - /* We add CPCL data and crop the TAG and the data length at the start and the trailer at the end */ - for (unsigned long i = 3; i < dwRecvLength-2; i++) { - v.push_back(static_cast<byte>(pbRecvBufferFat[i])); - } - burn(pbRecvBuffer, sizeof(pbRecvBuffer)); - burn(pbRecvBufferFat, sizeof(pbRecvBufferFat)); - - } - - /* Getting an ICC Public Key Certificates and an Issuer Public Key Certificates for the first application with the cpcl - * data present on the card and finally merge it into one byte array */ - void IccDataExtractor::GettingAllCerts(int readerNumber, vector<byte> &v){ - - #ifdef TC_WINDOWS - if(!Initialized) - throw WinscardLibraryNotInitialized(); - #endif - - bool isEMV= false; - - ConnectCard(readerNumber); - - /* Test all the type of applications and get the certificates from the first one found */ - for(int i=0;i<sizeof(SELECT_TYPES)/sizeof(SELECT_TYPES[0]); i++){ - - /* The card does not contain this application (0:Mastercard, 1:Visa, 2:Amex) */ - if(!TestingCardType(i)) continue; - isEMV= true; - GetCerts(v); - break; - } - - /* Need to disconnect reconnect the card to access CPLC data (not located in any application) */ - DisconnectCard(); - - /* Check if the card is not an EMV one */ - if(!isEMV) - throw EMVUnknownCardType(); - - ConnectCard(readerNumber); - - GetCPCL(v); - - DisconnectCard(); - } - - /* Getting the PAN by parsing the application - * (!NEED TO TEST CARD TYPE TO SELECT APPLICATION FIRST!)*/ - void IccDataExtractor::GetPAN(vector<byte> &v) { - - bool PANFound= false; - shared_ptr<TLVNode> node; - shared_ptr<TLVNode> PAN; - - BYTE pbRecvBuffer[64]; /* Buffer to receive the card response */ - BYTE pbRecvBufferFat[256]; /* Bigger buffer to receive the card response */ - - DWORD dwSendLength; /* Size of the send buffer */ - DWORD dwRecvLength; /* Size of the reception buffer */ - - /* Set up the io request */ - SCARD_IO_REQUEST ioRequest; - ioRequest.dwProtocol = dwActiveProtocol; - ioRequest.cbPciLength = sizeof(ioRequest); - - LONG returnValue; - - /* Parsing root folders */ - for (int sfi = 0; sfi < 32; sfi++) - { - /* Parsing sub folders */ - for (int rec = 0; rec < 17; rec++) - { - BYTE SELECT_APDU_FILE[] = {00, 0xB2, static_cast<unsigned char>(rec), static_cast<unsigned char>((sfi << 3) | 4), 0x00}; - - dwSendLength = sizeof(SELECT_APDU_FILE); - dwRecvLength = sizeof(pbRecvBuffer); - - /* Check if there is data in the folder */ - #ifdef TC_WINDOWS - returnValue = WSCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength,NULL, pbRecvBuffer, &dwRecvLength); - #else - returnValue = SCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength,NULL, pbRecvBuffer, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* There is no data in the folder */ - if (pbRecvBuffer[0] != 0x6C) - continue; - - /* It set the proper expected length of the data in the APDU */ - SELECT_APDU_FILE[4] = pbRecvBuffer[1]; - - dwRecvLength = sizeof(pbRecvBufferFat); - - /* Get the data from the folder */ - #ifdef TC_WINDOWS - returnValue = WSCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength,NULL, pbRecvBufferFat, &dwRecvLength); - #else - returnValue = SCardTransmit(hCard, &ioRequest, SELECT_APDU_FILE, dwSendLength,NULL, pbRecvBufferFat, &dwRecvLength); - #endif - - /* Check if the transmission was unsuccessful */ - if (returnValue != SCARD_S_SUCCESS) - throw PCSCException(returnValue); - - /* It received a response. Check if it didn't get a recognisable response */ - if (dwRecvLength < 2) - continue; - - /* Parsing the TLV */ - try{ - node = TLVParser::TLV_Parse(pbRecvBufferFat,sizeof(pbRecvBufferFat)); - }catch(TLVException){ - continue; - } - - /* Finding the PAN */ - try{ - PAN = TLVParser::TLV_Find(node, 0x5A); - }catch(TLVException){ - continue; - } - if(PAN) { - PANFound=true; - if (PAN->Length >= 8){ - for (int i = 6; i < 8;i++) { - v.push_back(static_cast<byte>(PAN->Value[i])); - } - } - } - - if(PANFound){ - burn(pbRecvBuffer, sizeof(pbRecvBuffer)); - burn(pbRecvBufferFat, sizeof(pbRecvBufferFat)); - return ; - } - } - } - burn(pbRecvBuffer, sizeof(pbRecvBuffer)); - burn(pbRecvBufferFat, sizeof(pbRecvBufferFat)); - throw EMVPANNotFound(); - } - - /* Helper function to transform the PAN received (vector of byte) to a string */ - template<typename TInputIter> - void IccDataExtractor::make_hex_string(TInputIter first, TInputIter last, string& returnValue, bool use_uppercase, bool insert_spaces) { - ostringstream ss; - ss << hex << std::setfill('0'); - if (use_uppercase) - ss << uppercase; - while (first != last) - { - ss << setw(2) << static_cast<int>(*first++); - if (insert_spaces && first != last) - ss << " "; - } - - returnValue = ss.str(); - } - - /* Wrapper function to get the PAN of the card*/ - void IccDataExtractor::GettingPAN(int readerNumber, string& panString) { - - #ifdef TC_WINDOWS - if(!Initialized) - throw WinscardLibraryNotInitialized(); - #endif - - vector<byte> PAN; - - bool isEMV= false; - - ConnectCard(readerNumber); - - /* Test all the type of applications and get the PAN from the first one found */ - for(int i=0;i<sizeof(SELECT_TYPES)/sizeof(SELECT_TYPES[0]); i++){ - - /* The card does not contain this application (0:Mastercard, 1:Visa, 2:Amex) */ - if(!TestingCardType(i)) continue; - isEMV=true; - GetPAN(PAN); - break; - } - - DisconnectCard(); - - /* Check if the card is not an EMV one */ - if(!isEMV) - throw EMVUnknownCardType(); - - make_hex_string(PAN.begin(),PAN.end(),panString); - - burn(&PAN.front(),PAN.size()); - } - - PCSCException::operator string() const{ - if (ErrorCode == SCARD_S_SUCCESS) - return string(); - - static const struct{ - LONG ErrorCode; - const char* ErrorString; - } ErrorStrings[] = { - #define SC_ERR(CODE) { CODE, #CODE }, -#ifdef TC_WINDOWS - SC_ERR(ERROR_BROKEN_PIPE) - SC_ERR(SCARD_E_NO_PIN_CACHE) - SC_ERR(SCARD_E_PIN_CACHE_EXPIRED) - SC_ERR(SCARD_E_READ_ONLY_CARD) - SC_ERR(SCARD_W_CACHE_ITEM_NOT_FOUND) - SC_ERR(SCARD_W_CACHE_ITEM_STALE) - SC_ERR(SCARD_W_CACHE_ITEM_TOO_BIG) -#endif - SC_ERR(SCARD_E_BAD_SEEK) - SC_ERR(SCARD_E_CANCELLED) - SC_ERR(SCARD_E_CANT_DISPOSE) - SC_ERR(SCARD_E_CARD_UNSUPPORTED) - SC_ERR(SCARD_E_CERTIFICATE_UNAVAILABLE) - SC_ERR(SCARD_E_COMM_DATA_LOST) - SC_ERR(SCARD_E_COMM_DATA_LOST) - SC_ERR(SCARD_E_DIR_NOT_FOUND) - SC_ERR(SCARD_E_DUPLICATE_READER) - SC_ERR(SCARD_E_FILE_NOT_FOUND) - SC_ERR(SCARD_E_ICC_CREATEORDER) - SC_ERR(SCARD_E_ICC_INSTALLATION) - SC_ERR(SCARD_E_INSUFFICIENT_BUFFER) - SC_ERR(SCARD_E_INVALID_ATR) - SC_ERR(SCARD_E_INVALID_CHV) - SC_ERR(SCARD_E_INVALID_HANDLE) - SC_ERR(SCARD_E_INVALID_PARAMETER) - SC_ERR(SCARD_E_INVALID_TARGET) - SC_ERR(SCARD_E_INVALID_VALUE) - SC_ERR(SCARD_E_NO_ACCESS) - SC_ERR(SCARD_E_NO_DIR) - SC_ERR(SCARD_E_NO_FILE) - SC_ERR(SCARD_E_NO_KEY_CONTAINER) - SC_ERR(SCARD_E_NO_MEMORY) - SC_ERR(SCARD_E_NO_READERS_AVAILABLE) - SC_ERR(SCARD_E_NO_SERVICE) - SC_ERR(SCARD_E_NO_SMARTCARD) - SC_ERR(SCARD_E_NO_SUCH_CERTIFICATE) - SC_ERR(SCARD_E_NOT_READY) - SC_ERR(SCARD_E_NOT_TRANSACTED) - SC_ERR(SCARD_E_PCI_TOO_SMALL) - SC_ERR(SCARD_E_PROTO_MISMATCH) - SC_ERR(SCARD_E_READER_UNAVAILABLE) - SC_ERR(SCARD_E_READER_UNSUPPORTED) - SC_ERR(SCARD_E_SERVER_TOO_BUSY) - SC_ERR(SCARD_E_SERVICE_STOPPED) - SC_ERR(SCARD_E_SHARING_VIOLATION) - SC_ERR(SCARD_E_SYSTEM_CANCELLED) - SC_ERR(SCARD_E_TIMEOUT) - SC_ERR(SCARD_E_UNEXPECTED) - SC_ERR(SCARD_E_UNKNOWN_CARD) - SC_ERR(SCARD_E_UNKNOWN_READER) - SC_ERR(SCARD_E_UNKNOWN_RES_MNG) - SC_ERR(SCARD_E_UNSUPPORTED_FEATURE) - SC_ERR(SCARD_E_WRITE_TOO_MANY) - SC_ERR(SCARD_F_COMM_ERROR) - SC_ERR(SCARD_F_INTERNAL_ERROR) - SC_ERR(SCARD_F_UNKNOWN_ERROR) - SC_ERR(SCARD_W_CANCELLED_BY_USER) - SC_ERR(SCARD_W_CARD_NOT_AUTHENTICATED) - SC_ERR(SCARD_W_CHV_BLOCKED) - SC_ERR(SCARD_W_EOF) - SC_ERR(SCARD_W_REMOVED_CARD) - SC_ERR(SCARD_W_RESET_CARD) - SC_ERR(SCARD_W_SECURITY_VIOLATION) - SC_ERR(SCARD_W_UNPOWERED_CARD) - SC_ERR(SCARD_W_UNRESPONSIVE_CARD) - SC_ERR(SCARD_W_UNSUPPORTED_CARD) - SC_ERR(SCARD_W_WRONG_CHV) -#undef SC_ERR - }; - - for (size_t i = 0; i < array_capacity(ErrorStrings); ++i) - { - if (ErrorStrings[i].ErrorCode == ErrorCode) - return ErrorStrings[i].ErrorString; - } - - stringstream s; - s << "0x" << ErrorCode; - return s.str(); - } - - #ifdef TC_HEADER_Common_Exception - void PCSCException::Show(HWND parent) const - { - string errorString = string(*this); - - if (!errorString.empty()) - { - wstringstream subjectErrorCode; - if (SubjectErrorCodeValid) - subjectErrorCode << L": " << SubjectErrorCode; - - if (!GetDictionaryValue(errorString.c_str())) - { - if (errorString.find("SCARD_E_") == 0 || errorString.find("SCARD_F_") == 0 || errorString.find("SCARD_W_") == 0) - { - errorString = errorString.substr(8); - for (size_t i = 0; i < errorString.size(); ++i) - { - if (errorString[i] == '_') - errorString[i] = ' '; - } - } - wchar_t err[8192]; - StringCbPrintfW(err, sizeof(err), L"%s:\n\n%hs%s", GetString("PCSC_ERROR"), errorString.c_str(), subjectErrorCode.str().c_str()); - ErrorDirect(err, parent); - } - else - { - wstring err = GetString(errorString.c_str()); - - if (SubjectErrorCodeValid) - err += L"\n\nError code" + subjectErrorCode.str(); - - ErrorDirect(err.c_str(), parent); - } - } - } - #endif // TC_HEADER_Common_Exception - -#ifdef TC_HEADER_Platform_Exception - - void PCSCException::Deserialize(shared_ptr <Stream> stream) - { - Exception::Deserialize(stream); - Serializer sr(stream); - uint64 code; - sr.Deserialize("ErrorCode", code); - sr.Deserialize("SubjectErrorCodeValid", SubjectErrorCodeValid); - sr.Deserialize("SubjectErrorCode", SubjectErrorCode); - ErrorCode = (LONG)code; - } - - void PCSCException::Serialize(shared_ptr <Stream> stream) const - { - Exception::Serialize(stream); - Serializer sr(stream); - sr.Serialize("ErrorCode", (uint64)ErrorCode); - sr.Serialize("SubjectErrorCodeValid", SubjectErrorCodeValid); - sr.Serialize("SubjectErrorCode", SubjectErrorCode); - } - -# define TC_EXCEPTION(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) -# undef TC_EXCEPTION_NODECL -# define TC_EXCEPTION_NODECL(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) - - TC_SERIALIZER_FACTORY_ADD_EXCEPTION_SET(PCSCTokenException); - -#endif - -}
\ No newline at end of file diff --git a/src/Common/IccDataExtractor.h b/src/Common/IccDataExtractor.h index 777a3555..e69de29b 100644 --- a/src/Common/IccDataExtractor.h +++ b/src/Common/IccDataExtractor.h @@ -1,231 +0,0 @@ -// -// Created by bshp on 1/14/23. -// - -#ifndef NEWEMV_ICCDATAEXTRACTOR_H -#define NEWEMV_ICCDATAEXTRACTOR_H - -#include <iostream> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sstream> -#include <vector> -#include <iomanip> -#include <memory> -#include "Platform/PlatformBase.h" -#include "TLVParser.h" - -#ifdef __linux__ -#include <unistd.h> -#endif - -#if defined (TC_WINDOWS) && !defined (TC_PROTOTYPE) -# include "Exception.h" -#else -# include "Platform/Exception.h" -#endif - -#ifdef TC_WINDOWS -#include <winscard.h> -#include <windows.h> -#endif -#ifdef TC_UNIX -#undef BOOL -#include <PCSC/winscard.h> -using VeraCrypt::byte; -#define BOOL int -//#include <unistd.h> //Works without on windows -#endif - -#ifdef _WIN32 -#include <windows.h> -#endif - -#ifdef _WIN64 -#define ssize_t __int64 -#else -#define ssize_t long -#endif - -#define SELECT_TYPE_SIZE 12 /* Size of the SELECT_TYPE APDU */ - -/* Winscard function pointers definitions for windows import */ -#ifdef TC_WINDOWS -typedef LONG (WINAPI *SCardEstablishContextPtr)(DWORD dwScope,LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext); -typedef LONG (WINAPI *SCardReleaseContextPtr)(SCARDCONTEXT hContext); -typedef LONG (WINAPI *SCardConnectAPtr)(SCARDCONTEXT hContext,LPCSTR szReader,DWORD dwShareMode,DWORD dwPreferredProtocols,LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); -typedef LONG (WINAPI *SCardDisconnectPtr)(SCARDHANDLE hCard, DWORD dwDisposition); -typedef LONG (WINAPI *SCardTransmitPtr)(SCARDHANDLE hCard,LPCSCARD_IO_REQUEST pioSendPci,const BYTE* pbSendBuffer, DWORD cbSendLength,LPSCARD_IO_REQUEST pioRecvPci,BYTE* pbRecvBuffer, LPDWORD pcbRecvLength); -typedef LONG (WINAPI *SCardListReadersAPtr)(SCARDCONTEXT hContext,LPCSTR mszGroups,LPSTR mszReaders, LPDWORD pcchReaders); -typedef LONG (WINAPI *SCardFreeMemoryPtr)(SCARDCONTEXT hContext,LPCVOID pvMem); -#endif - -namespace VeraCrypt -{ - class IccDataExtractor { - private: - - /* Used for loading winscard on windows */ - #ifdef TC_WINDOWS - /* Winscard Library Handle */ - HMODULE WinscardLibraryHandle; - - /* Winscard function pointers */ - SCardEstablishContextPtr WSCardEstablishContext; - SCardReleaseContextPtr WSCardReleaseContext; - SCardConnectAPtr WSCardConnectA; - SCardDisconnectPtr WSCardDisconnect; - SCardFreeMemoryPtr WSCardFreeMemory; - SCardListReadersAPtr WSCardListReadersA; - SCardTransmitPtr WSCardTransmit; - - /* Is the winscard library loaded */ - static bool Initialized; - #endif - - /* SELECT_TYPES FOR DIFFERENT AIDs*/ - const static BYTE SELECT_MASTERCARD[SELECT_TYPE_SIZE]; - const static BYTE SELECT_VISA[SELECT_TYPE_SIZE]; - const static BYTE SELECT_AMEX[SELECT_TYPE_SIZE]; - const static BYTE * SELECT_TYPES[3]; - - - SCARDCONTEXT hContext; /* Handle that identifies the resource manager context.*/ - - SCARDHANDLE hCard; /* A handle that identifies the connection to the smart card in the designated reader*/ - - std::vector<char*> readers; /* Card reader list */ - - unsigned long int nbReaders; /* Number of connected (available) readers */ - - LPSTR mszReaders; /* Names of the reader groups defined to the system, as a multi-string. Use a NULL value to - * list all readers in the system */ - - DWORD dwActiveProtocol; /* A flag that indicates the established active protocol. - * SCARD_PROTOCOL_T0: An asynchronous, character-oriented half-duplex transmission protocol. - * SCARD_PROTOCOL_T1: An asynchronous, block-oriented half-duplex transmission protocol.*/ - - - /* Establishing the resource manager context (the scope) within which database operations are performed. - * The module of the smart card subsystem that manages access to multiple readers and smart cards. The - * resource manager identifies and tracks resources, allocates readers and resources across multiple - * applications,and supports transaction primitives for accessing services available on a given card.*/ - int EstablishRSContext(); - - /* Connecting to the card in the given reader*/ - int ConnectCard(unsigned long int reader_nb); - - /* Disconnect the card currently connected*/ - int DisconnectCard(); - - /* Testing if the card contains the application of the given EMV type */ - bool TestingCardType(const int SELECT_TYPE_NUMBER); - - /* Getting the ICC Public Key Certificates and the Issuer Public Key Certificates by parsing the application - * (!NEED TO TEST CARD TYPE TO SELECT APPLICATION FIRST!)*/ - void GetCerts(vector<byte> &CERTS); - - /* Getting CPCL data from the card and put it into a reference*/ - void GetCPCL(vector<byte> &v); - - /* Getting the PAN by parsing the application - * (!NEED TO TEST CARD TYPE TO SELECT APPLICATION FIRST!)*/ - void GetPAN(vector<byte> &v); - - /* Helper function to make a string from plain arrays and various standard containers of bytes */ - template<typename TInputIter> - void make_hex_string(TInputIter first, TInputIter last, std::string& panString, bool use_uppercase = true, bool insert_spaces = false); - - public: - IccDataExtractor(); - - ~IccDataExtractor(); - - /* Used to initialize the winscard library on windows to make sure the dll is in System32 */ - #ifdef TC_WINDOWS - void IccDataExtractor::InitLibrary(); - #endif - - /* Detecting available readers and filling the reader table. Returns - * the number of available readers */ - unsigned long GetReaders(); - - - /* Getting an ICC Public Key Certificates, an Issuer Public Key Certificates and the CPCL data - * from the card designated by the reader number. Appending them into a byte vector */ - void GettingAllCerts(int readerNumber, vector<byte> &v); - - /* Getting the PAN from the card designated by the reader number */ - void GettingPAN(int readerNumber, string& panString); - }; - - struct PCSCException: public Exception - { - PCSCException(LONG errorCode = (LONG) -1): ErrorCode(errorCode), SubjectErrorCodeValid(false), SubjectErrorCode((uint64)-1){} - PCSCException(LONG errorCode, uint64 subjectErrorCode): ErrorCode(errorCode), SubjectErrorCodeValid(true), SubjectErrorCode(subjectErrorCode){} - - #ifdef TC_HEADER_Platform_Exception - virtual ~PCSCException() throw () { } - TC_SERIALIZABLE_EXCEPTION(PCSCException); - #else - - void Show(HWND parent) const; - #endif - - operator string () const; - LONG GetErrorCode() const { return ErrorCode; } - - protected: - LONG ErrorCode; - bool SubjectErrorCodeValid; - uint64 SubjectErrorCode; - }; - - #ifdef TC_HEADER_Platform_Exception - - #define TC_EXCEPTION(NAME) TC_EXCEPTION_DECL(NAME,Exception) - - #undef TC_EXCEPTION_SET - #define TC_EXCEPTION_SET \ - TC_EXCEPTION_NODECL (PCSCException); \ - TC_EXCEPTION (WinscardLibraryNotInitialized); \ - TC_EXCEPTION (InvalidEMVPath); \ - TC_EXCEPTION (EMVKeyfileDataNotFound); \ - TC_EXCEPTION (EMVPANNotFound); \ - TC_EXCEPTION (EMVUnknownCardType); - TC_EXCEPTION_SET; - - #undef TC_EXCEPTION - - #else // !TC_HEADER_Platform_Exception - - struct WinscardLibraryNotInitialized: public Exception - { - void Show(HWND parent) const { Error("WINSCARD_MODULE_INIT_FAILED", parent); } - }; - - struct InvalidEMVPath: public Exception - { - void Show(HWND parent) const { Error("INVALID_EMV_PATH", parent); } - }; - - struct EMVKeyfileDataNotFound: public Exception - { - void Show(HWND parent) const { Error("EMV_KEYFILE_DATA_NOT_FOUND", parent); } - }; - - struct EMVPANNotFound: public Exception - { - void Show(HWND parent) const { Error("EMV_PAN_NOT_FOUND", parent); } - }; - - struct EMVUnknownCardType: public Exception - { - void Show(HWND parent) const { Error("EMV_UNKNOWN_CARD_TYPE", parent); } - }; - - #endif // !TC_HEADER_Platform_Exception -} - -#endif //NEWEMV_ICCDATAEXTRACTOR_H diff --git a/src/Common/Keyfiles.c b/src/Common/Keyfiles.c index d2207f5f..d5a2d5c2 100644 --- a/src/Common/Keyfiles.c +++ b/src/Common/Keyfiles.c @@ -250,7 +250,7 @@ BOOL KeyFilesApply (HWND hwndDlg, Password *password, KeyFile *firstKeyFile, con // Determine whether it's a security token path try { - if (Token::IsKeyfilePathValid (kf->FileName, ActivateEMVOption)) + if (Token::IsKeyfilePathValid (kf->FileName, EMVSupportEnabled)) { // Apply security token keyfile vector <byte> keyfileData; diff --git a/src/Common/Language.xml b/src/Common/Language.xml index cd996bce..a34187a5 100644 --- a/src/Common/Language.xml +++ b/src/Common/Language.xml @@ -113,7 +113,6 @@ <entry lang="en" key="IDC_CACHE">Cache passwords and keyfil&es in memory</entry> <entry lang="en" key="IDC_CLOSE_BKG_TASK_WHEN_NOVOL">Exit when there are no mounted volumes</entry> <entry lang="en" key="IDC_CLOSE_TOKEN_SESSION_AFTER_MOUNT">&Close token session (log out) after a volume is successfully mounted</entry> - <entry lang="en" key="IDC_ACTIVATE_EMV_OPTION">Activate EMV Option</entry> <entry lang="en" key="IDC_COPY_EXPANDER">Include VeraCrypt Volume Expander</entry> <entry lang="en" key="IDC_COPY_WIZARD">Include VeraCrypt Volume Creation Wizard</entry> <entry lang="en" key="IDC_CREATE">Create</entry> @@ -1280,12 +1279,6 @@ <entry lang="en" key="CKR_PIN_INCORRECT">Password for security token is incorrect.</entry> <entry lang="en" key="CKR_DEVICE_MEMORY">The security token does not have enough memory/space to perform the requested operation.\n\nIf you are attempting to import a keyfile, you should select a smaller file or use a keyfile generated by VeraCrypt (select 'Tools' > 'Keyfile Generator').</entry> <entry lang="en" key="ALL_TOKEN_SESSIONS_CLOSED">All open security token sessions have been closed.</entry> - <entry lang="en" key="WINSCARD_MODULE_INIT_FAILED">Error when loading the winscard library.\n\nPlease make sure the WinSCard.dll is present in your System32 folder.</entry> - <entry lang="en" key="INVALID_EMV_PATH">EMV path is invalid.</entry> - <entry lang="en" key="EMV_KEYFILE_DATA_NOT_FOUND">Unable to build a keyfile from the EMV card's data.\n\nOne of the following is missing:\n- ICC Public Key Certificate.\n- Issuer Public Key Certificate.\n- CPCL data.</entry> - <entry lang="en" key="EMV_PAN_NOT_FOUND">No Primary Account Number (PAN) found in the EMV card.</entry> - <entry lang="en" key="EMV_UNKNOWN_CARD_TYPE">The card in the reader is not a supported EMV card.</entry> - <entry lang="en" key="SCARD_W_REMOVED_CARD">No card in the reader.\n\nPlease make sure the card is correctly slotted.</entry> <entry lang="en" key="SELECT_TOKEN_KEYFILES">Select Security Token Keyfiles</entry> <entry lang="en" key="TOKEN_SLOT_ID">Slot</entry> <entry lang="en" key="TOKEN_NAME">Token name</entry> @@ -1616,6 +1609,19 @@ <entry lang="en" key="FULL_FORMAT">Full Format</entry> <entry lang="en" key="FAST_CREATE">Fast Create</entry> <entry lang="en" key="WARN_FAST_CREATE">WARNING: You should use Fast Create only in the following cases:\n\n1) The device contains no sensitive data and you do not need plausible deniability.\n2) The device has already been securely and fully encrypted.\n\nAre you sure you want to use Fast Create?</entry> + <entry lang="en" key="IDC_ENABLE_EMV_SUPPORT">Enable EMV Support</entry> + <entry lang="en" key="COMMAND_APDU_INVALID">The APDU command sent to the card is not valid.</entry> + <entry lang="en" key="EXTENDED_APDU_UNSUPPORTED">Extended APDU commands cannot be used with the current token.</entry> + <entry lang="en" key="SCARD_MODULE_INIT_FAILED">Error when loading the WinSCard / PCSC library.</entry> + <entry lang="en" key="EMV_UNKNOWN_CARD_TYPE">The card in the reader is not a supported EMV card.</entry> + <entry lang="en" key="EMV_SELECT_AID_FAILED">The AID of the card in the reader could not be selected.</entry> + <entry lang="en" key="EMV_ICC_CERT_NOTFOUND">ICC Public Key Certificate was not found in the card.</entry> + <entry lang="en" key="EMV_ISSUER_CERT_NOTFOUND">Issuer Public Key Certificate was not found in the card.</entry> + <entry lang="en" key="EMV_CPLC_NOTFOUND">CLPC was not found in the EMV card.</entry> + <entry lang="en" key="EMV_PAN_NOTFOUND">No Primary Account Number (PAN) found in the EMV card.</entry> + <entry lang="en" key="INVALID_EMV_PATH">EMV path is invalid.</entry> + <entry lang="en" key="EMV_KEYFILE_DATA_NOTFOUND">Unable to build a keyfile from the EMV card's data.\n\nOne of the following is missing:\n- ICC Public Key Certificate.\n- Issuer Public Key Certificate.\n- CPCL data.</entry> + <entry lang="en" key="SCARD_W_REMOVED_CARD">No card in the reader.\n\nPlease make sure the card is correctly slotted.</entry> </localization> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="VeraCrypt"> diff --git a/src/Common/PCSCException.cpp b/src/Common/PCSCException.cpp new file mode 100644 index 00000000..93fff181 --- /dev/null +++ b/src/Common/PCSCException.cpp @@ -0,0 +1,199 @@ +#include "PCSCException.h" + +#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 + +namespace VeraCrypt +{ + PCSCException::operator string() const + { + if (ErrorCode == SCARD_S_SUCCESS) + return string(); + + static const struct{ + LONG_PCSC ErrorCode; + const char* ErrorString; + } ErrorStrings[] = { +#define SC_ERR(CODE) { CODE, #CODE }, +#ifdef TC_WINDOWS + SC_ERR(ERROR_BROKEN_PIPE) + SC_ERR(SCARD_E_NO_PIN_CACHE) + SC_ERR(SCARD_E_PIN_CACHE_EXPIRED) + SC_ERR(SCARD_E_READ_ONLY_CARD) + SC_ERR(SCARD_W_CACHE_ITEM_NOT_FOUND) + SC_ERR(SCARD_W_CACHE_ITEM_STALE) + SC_ERR(SCARD_W_CACHE_ITEM_TOO_BIG) +#endif + SC_ERR(SCARD_E_BAD_SEEK) + SC_ERR(SCARD_E_CANCELLED) + SC_ERR(SCARD_E_CANT_DISPOSE) + SC_ERR(SCARD_E_CARD_UNSUPPORTED) + SC_ERR(SCARD_E_CERTIFICATE_UNAVAILABLE) + SC_ERR(SCARD_E_COMM_DATA_LOST) + SC_ERR(SCARD_E_COMM_DATA_LOST) + SC_ERR(SCARD_E_DIR_NOT_FOUND) + SC_ERR(SCARD_E_DUPLICATE_READER) + SC_ERR(SCARD_E_FILE_NOT_FOUND) + SC_ERR(SCARD_E_ICC_CREATEORDER) + SC_ERR(SCARD_E_ICC_INSTALLATION) + SC_ERR(SCARD_E_INSUFFICIENT_BUFFER) + SC_ERR(SCARD_E_INVALID_ATR) + SC_ERR(SCARD_E_INVALID_CHV) + SC_ERR(SCARD_E_INVALID_HANDLE) + SC_ERR(SCARD_E_INVALID_PARAMETER) + SC_ERR(SCARD_E_INVALID_TARGET) + SC_ERR(SCARD_E_INVALID_VALUE) + SC_ERR(SCARD_E_NO_ACCESS) + SC_ERR(SCARD_E_NO_DIR) + SC_ERR(SCARD_E_NO_FILE) + SC_ERR(SCARD_E_NO_KEY_CONTAINER) + SC_ERR(SCARD_E_NO_MEMORY) + SC_ERR(SCARD_E_NO_READERS_AVAILABLE) + SC_ERR(SCARD_E_NO_SERVICE) + SC_ERR(SCARD_E_NO_SMARTCARD) + SC_ERR(SCARD_E_NO_SUCH_CERTIFICATE) + SC_ERR(SCARD_E_NOT_READY) + SC_ERR(SCARD_E_NOT_TRANSACTED) + SC_ERR(SCARD_E_PCI_TOO_SMALL) + SC_ERR(SCARD_E_PROTO_MISMATCH) + SC_ERR(SCARD_E_READER_UNAVAILABLE) + SC_ERR(SCARD_E_READER_UNSUPPORTED) + SC_ERR(SCARD_E_SERVER_TOO_BUSY) + SC_ERR(SCARD_E_SERVICE_STOPPED) + SC_ERR(SCARD_E_SHARING_VIOLATION) + SC_ERR(SCARD_E_SYSTEM_CANCELLED) + SC_ERR(SCARD_E_TIMEOUT) + SC_ERR(SCARD_E_UNEXPECTED) + SC_ERR(SCARD_E_UNKNOWN_CARD) + SC_ERR(SCARD_E_UNKNOWN_READER) + SC_ERR(SCARD_E_UNKNOWN_RES_MNG) + SC_ERR(SCARD_E_UNSUPPORTED_FEATURE) + SC_ERR(SCARD_E_WRITE_TOO_MANY) + SC_ERR(SCARD_F_COMM_ERROR) + SC_ERR(SCARD_F_INTERNAL_ERROR) + SC_ERR(SCARD_F_UNKNOWN_ERROR) + SC_ERR(SCARD_W_CANCELLED_BY_USER) + SC_ERR(SCARD_W_CARD_NOT_AUTHENTICATED) + SC_ERR(SCARD_W_CHV_BLOCKED) + SC_ERR(SCARD_W_EOF) + SC_ERR(SCARD_W_REMOVED_CARD) + SC_ERR(SCARD_W_RESET_CARD) + SC_ERR(SCARD_W_SECURITY_VIOLATION) + SC_ERR(SCARD_W_UNPOWERED_CARD) + SC_ERR(SCARD_W_UNRESPONSIVE_CARD) + SC_ERR(SCARD_W_UNSUPPORTED_CARD) + SC_ERR(SCARD_W_WRONG_CHV) +#undef SC_ERR + }; + + for (size_t i = 0; i < array_capacity(ErrorStrings); ++i) + { + if (ErrorStrings[i].ErrorCode == ErrorCode) + return ErrorStrings[i].ErrorString; + } + + stringstream s; + s << "0x" << ErrorCode; + return s.str(); + } + +#ifdef TC_HEADER_Common_Exception + void PCSCException::Show(HWND parent) const + { + string errorString = string(*this); + + if (!errorString.empty()) + { + if (!GetDictionaryValue(errorString.c_str())) + { + if (errorString.find("SCARD_E_") == 0 || errorString.find("SCARD_F_") == 0 || errorString.find("SCARD_W_") == 0) + { + errorString = errorString.substr(8); + for (size_t i = 0; i < errorString.size(); ++i) + { + if (errorString[i] == '_') + errorString[i] = ' '; + } + } + wchar_t err[8192]; + StringCbPrintfW(err, sizeof(err), L"%s:\n\n%hs%s", GetString("PCSC_ERROR"), errorString.c_str()); + ErrorDirect(err, parent); + } + else + { + wstring err = GetString(errorString.c_str()); + ErrorDirect(err.c_str(), parent); + } + } + } +#endif // TC_HEADER_Common_Exception + +#ifdef TC_HEADER_Platform_Exception + + void PCSCException::Deserialize(shared_ptr <Stream> stream) + { + Exception::Deserialize(stream); + Serializer sr(stream); + sr.Deserialize("ErrorCode", ErrorCode); + } + + void PCSCException::Serialize(shared_ptr <Stream> stream) const + { + Exception::Serialize(stream); + Serializer sr(stream); + sr.Serialize("ErrorCode", ErrorCode); + } + +# define TC_EXCEPTION(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) +# undef TC_EXCEPTION_NODECL +# define TC_EXCEPTION_NODECL(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) + + TC_SERIALIZER_FACTORY_ADD_EXCEPTION_SET(PCSCException); + +#endif + + CommandAPDUNotValid::operator string() const + { + return string(ErrorStr); + } + +#ifdef TC_HEADER_Common_Exception + void CommandAPDUNotValid::Show(HWND parent) const + { + string msgBody = "Command APDU invalid.\n\n\n(If you report a bug in connection with this, please include the following technical information in the bug report:\n" + SrcPos + "\nLast Error = " + ErrorStr + ")"; + MessageBoxA (parent, msgBody.c_str(), "VeraCrypt", MB_ICONERROR | MB_SETFOREGROUND); + } +#endif // TC_HEADER_Common_Exception + +#ifdef TC_HEADER_Platform_Exception + + void CommandAPDUNotValid::Deserialize(shared_ptr <Stream> stream) + { + Exception::Deserialize(stream); + Serializer sr(stream); + sr.Deserialize("SrcPos", SrcPos); + sr.Deserialize("ErrorStr", ErrorStr); + } + + void CommandAPDUNotValid::Serialize(shared_ptr <Stream> stream) const + { + Exception::Serialize(stream); + Serializer sr(stream); + sr.Serialize("SrcPos", SrcPos); + sr.Serialize("ErrorStr", ErrorStr); + } + +# define TC_EXCEPTION(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) +# undef TC_EXCEPTION_NODECL +# define TC_EXCEPTION_NODECL(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) + + TC_SERIALIZER_FACTORY_ADD_EXCEPTION_SET(CommandAPDUNotValid); + +#endif +}
\ No newline at end of file diff --git a/src/Common/PCSCException.h b/src/Common/PCSCException.h new file mode 100644 index 00000000..6c7766b5 --- /dev/null +++ b/src/Common/PCSCException.h @@ -0,0 +1,136 @@ +#ifndef TC_HEADER_Common_PCSCException +#define TC_HEADER_Common_PCSCException + +#include "Platform/PlatformBase.h" +#if defined (TC_WINDOWS) && !defined (TC_PROTOTYPE) +# include "Exception.h" +#else +# include "Platform/Exception.h" +#endif + +#include "SCardLoader.h" + +#ifdef TC_MACOSX +#define LONG_PCSC uint32_t +#else +#define LONG_PCSC LONG +#endif + +namespace VeraCrypt +{ + struct PCSCException: public Exception + { + PCSCException(LONG_PCSC errorCode = (LONG_PCSC)-1): ErrorCode(errorCode){} + +#ifdef TC_HEADER_Platform_Exception + virtual ~PCSCException() throw () { } + TC_SERIALIZABLE_EXCEPTION(PCSCException); +#else + void Show(HWND parent) const; +#endif + + operator string () const; + LONG_PCSC GetErrorCode() const { return ErrorCode; } + + protected: + LONG_PCSC ErrorCode; + }; + + struct CommandAPDUNotValid: public Exception + { + CommandAPDUNotValid() : SrcPos (""), ErrorStr ("") { } + CommandAPDUNotValid(const string& srcPos, const string& errorStr) : SrcPos (srcPos), ErrorStr(errorStr) { } + +#ifdef TC_HEADER_Platform_Exception + virtual ~CommandAPDUNotValid() throw () { } + TC_SERIALIZABLE_EXCEPTION(CommandAPDUNotValid); +#else + void Show(HWND parent) const; +#endif + + operator string () const; + + protected: + string SrcPos; + string ErrorStr; + }; + +#ifdef TC_HEADER_Platform_Exception + + #define TC_EXCEPTION(NAME) TC_EXCEPTION_DECL(NAME,Exception) + + #undef TC_EXCEPTION_SET + #define TC_EXCEPTION_SET \ + TC_EXCEPTION_NODECL (PCSCException); \ + TC_EXCEPTION_NODECL (CommandAPDUNotValid); \ + TC_EXCEPTION (ExtendedAPDUNotSupported); \ + TC_EXCEPTION (ScardLibraryInitializationFailed); \ + TC_EXCEPTION (EMVUnknownCardType); \ + TC_EXCEPTION (EMVSelectAIDFailed); \ + TC_EXCEPTION (EMVIccCertNotFound); \ + TC_EXCEPTION (EMVIssuerCertNotFound); \ + TC_EXCEPTION (EMVCPLCNotFound); \ + TC_EXCEPTION (InvalidEMVPath); \ + TC_EXCEPTION (EMVKeyfileDataNotFound); \ + TC_EXCEPTION (EMVPANNotFound); \ + + TC_EXCEPTION_SET; + + #undef TC_EXCEPTION + +#else // !TC_HEADER_Platform_Exception + + struct ExtendedAPDUNotSupported: public Exception + { + void Show(HWND parent) const { Error("EXTENDED_APDU_UNSUPPORTED", parent); } + }; + + struct ScardLibraryInitializationFailed: public Exception + { + void Show(HWND parent) const { Error("SCARD_MODULE_INIT_FAILED", parent); } + }; + + struct EMVUnknownCardType: public Exception + { + void Show(HWND parent) const { Error("EMV_UNKNOWN_CARD_TYPE", parent); } + }; + + struct EMVSelectAIDFailed: public Exception + { + void Show(HWND parent) const { Error("EMV_SELECT_AID_FAILED", parent); } + }; + + struct EMVIccCertNotFound: public Exception + { + void Show(HWND parent) const { Error("EMV_ICC_CERT_NOTFOUND", parent); } + }; + + struct EMVIssuerCertNotFound: public Exception + { + void Show(HWND parent) const { Error("EMV_ISSUER_CERT_NOTFOUND", parent); } + }; + + struct EMVCPLCNotFound: public Exception + { + void Show(HWND parent) const { Error("EMV_CPLC_NOTFOUND", parent); } + }; + + struct EMVPANNotFound: public Exception + { + void Show(HWND parent) const { Error("EMV_PAN_NOTFOUND", parent); } + }; + + struct InvalidEMVPath: public Exception + { + void Show(HWND parent) const { Error("INVALID_EMV_PATH", parent); } + }; + + struct EMVKeyfileDataNotFound: public Exception + { + void Show(HWND parent) const { Error("EMV_KEYFILE_DATA_NOTFOUND", parent); } + }; + +#endif // !TC_HEADER_Platform_Exception +} + +#endif // TC_HEADER_Common_PCSCException
\ No newline at end of file diff --git a/src/Common/ResponseAPDU.cpp b/src/Common/ResponseAPDU.cpp new file mode 100644 index 00000000..8a7f069c --- /dev/null +++ b/src/Common/ResponseAPDU.cpp @@ -0,0 +1,111 @@ +#include "ResponseAPDU.h" +#include <string.h> + +using namespace std; + +namespace VeraCrypt +{ + uint16 BytesToUInt16(const vector<byte>& buff) + { + uint16 value = 0; + for (uint16 i = 0; i < buff.size(); i++) + { + value <<= 8; + value |= (uint16)buff.at(i); + } + + return value; + } + + void AppendData (vector<byte>& buffer, const byte* pbData, size_t cbData) + { + size_t orgSize = buffer.size (); + buffer.resize (orgSize + cbData); + memcpy (buffer.data () + orgSize, pbData, cbData); + } + + /*********************************************************************************/ + + void ResponseAPDU::clear() + { + m_data.clear(); + m_SW = 0; + } + + ResponseAPDU::ResponseAPDU() : m_SW(0) + { + } + + ResponseAPDU::ResponseAPDU(const vector<byte>& data, uint16 SW) + { + m_data = data; + m_SW = SW; + } + + uint32 ResponseAPDU::getNr() + { + return (uint32)m_data.size(); + } + + const vector<byte> ResponseAPDU::getData() + { + return m_data; + } + + byte ResponseAPDU::getSW1() + { + return (byte)((0xFF00 & m_SW) >> 8); + } + + byte ResponseAPDU::getSW2() + { + return (byte)(0x00FF & m_SW); + } + + uint16 ResponseAPDU::getSW() + { + return m_SW; + } + + const vector<byte> ResponseAPDU::getBytes() + { + vector<byte> apdu; + + AppendData(apdu, m_data.data(), m_data.size()); + apdu.push_back((byte)getSW1()); + apdu.push_back((byte)getSW2()); + + return apdu; + } + + void ResponseAPDU::appendData(const vector<byte>& data) + { + appendData(data.data(), data.size()); + } + + void ResponseAPDU::appendData(const byte* data, size_t dataLen) + { + AppendData(m_data, data, dataLen); + } + + void ResponseAPDU::setSW(uint16 SW) + { + m_SW = SW; + } + + void ResponseAPDU::setBytes(const vector<byte>& bytes) + { + clear(); + if (bytes.size() >= 2) + { + vector<byte> SWBytes; + m_data.resize(bytes.size() - 2); + SWBytes.resize(2); + + memcpy(m_data.data(), bytes.data(), bytes.size() - 2); + memcpy(SWBytes.data(), bytes.data() + bytes.size() - 2, 2); + m_SW = BytesToUInt16(SWBytes); + } + } +} + diff --git a/src/Common/ResponseAPDU.h b/src/Common/ResponseAPDU.h new file mode 100644 index 00000000..b9aa4adc --- /dev/null +++ b/src/Common/ResponseAPDU.h @@ -0,0 +1,44 @@ +#ifndef TC_HEADER_Common_ResponseAPDU +#define TC_HEADER_Common_ResponseAPDU + +#include "Platform/PlatformBase.h" + +namespace VeraCrypt +{ + class ResponseAPDU + { + protected: + + vector<byte> m_data; + + uint16 m_SW; + + public: + + void clear(); + + ResponseAPDU(); + + ResponseAPDU(const vector<byte>& data, uint16 SW); + + uint32 getNr(); + + const vector<byte> getData(); + + byte getSW1(); + + byte getSW2(); + + uint16 getSW(); + + const vector<byte> getBytes(); + + void setSW(uint16 SW); + void setBytes(const vector<byte>& bytes); + + void appendData(const vector<byte>& data); + void appendData(const byte* data, size_t dataLen); + }; +}; + +#endif // TC_HEADER_Common_ResponseAPDU
\ No newline at end of file diff --git a/src/Common/SCard.cpp b/src/Common/SCard.cpp new file mode 100644 index 00000000..9f8d1145 --- /dev/null +++ b/src/Common/SCard.cpp @@ -0,0 +1,62 @@ +#include "SCard.h" + +using namespace std; + +namespace VeraCrypt +{ + SCardManager SCard::manager; + + SCard::SCard() : m_reader(NULL) + { + } + + SCard::SCard(size_t slotId) + { + m_reader = SCard::manager.GetReader(slotId); + } + + SCard::~SCard() + { + if (m_reader) + { + m_reader->Disconnect(); + } + } + + SCard::SCard(const SCard& other) : m_reader(other.m_reader) + { + } + + SCard::SCard(SCard&& other) : m_reader(std::move(other.m_reader)) + { + } + + SCard& SCard::operator = (const SCard& other) + { + if (this != &other) + { + m_reader = other.m_reader; + } + return *this; + } + + SCard& SCard::operator = (SCard&& other) + { + if (this != &other) + { + m_reader = std::move(other.m_reader); + } + return *this; + } + + bool SCard::IsCardHandleValid() const + { + bool isValid = false; + if (m_reader) + { + isValid = m_reader->CardHandleStatus() == SCARD_S_SUCCESS; + } + + return isValid; + } +} diff --git a/src/Common/SCard.h b/src/Common/SCard.h new file mode 100644 index 00000000..b02f14f0 --- /dev/null +++ b/src/Common/SCard.h @@ -0,0 +1,27 @@ +#ifndef TC_HEADER_Common_SCard +#define TC_HEADER_Common_SCard + +#include "Platform/PlatformBase.h" + +#include "SCardManager.h" + +namespace VeraCrypt +{ + class SCard + { + protected: + shared_ptr<SCardReader> m_reader; + public: + static SCardManager manager; + SCard(); + SCard(size_t slotId); + SCard(const SCard& other); + SCard(SCard&& other); + SCard& operator = (const SCard& other); + SCard& operator = (SCard&& other); + virtual ~SCard(); + bool IsCardHandleValid() const; + }; +} + +#endif // TC_HEADER_Common_SCard diff --git a/src/Common/SCardLoader.cpp b/src/Common/SCardLoader.cpp new file mode 100644 index 00000000..ff3c9100 --- /dev/null +++ b/src/Common/SCardLoader.cpp @@ -0,0 +1,402 @@ +#include "SCardLoader.h" +#include "PCSCException.h" + +#ifndef TC_WINDOWS +#include <dlfcn.h> +#define LoadLibrary(x) dlopen(x, RTLD_NOW | RTLD_LOCAL) +#define FreeLibrary(x) dlclose(x) +#define GetProcAddress(x, y) dlsym(x, y) +typedef void* HMODULE; +#ifdef TC_MACOSX +#if !defined(USE_SCARD_CONTROL_112) +#define SCardControlName "SCardControl132" +#else +#define SCardControlName "SCardControl" +#endif +#else +#define SCardControlName "SCardControl" +#endif +#define SCardConnectName "SCardConnect" +#define SCardStatusName "SCardStatus" +#define SCardGetStatusChangeName "SCardGetStatusChange" +#define SCardListReaderGroupsName "SCardListReaderGroups" +#define SCardListReadersName "SCardListReaders" +#else +#define SCardControlName "SCardControl" +#define SCardConnectName "SCardConnectW" +#define SCardStatusName "SCardStatusW" +#define SCardGetStatusChangeName "SCardGetStatusChangeW" +#define SCardListReaderGroupsName "SCardListReaderGroupsW" +#define SCardListReadersName "SCardListReadersW" +#endif + +using namespace std; + +namespace VeraCrypt +{ + HMODULE SCardLoader::hScardModule = NULL; + SCARDCONTEXT SCardLoader::hScardContext = 0; + SCardEstablishContextPtr SCardLoader::scardEstablishContext = NULL; + SCardReleaseContextPtr SCardLoader::scardReleaseContext = NULL; + SCardIsValidContextPtr SCardLoader::scardIsValidContext = NULL; +#ifndef TC_MACOSX + SCardFreeMemoryPtr SCardLoader::scardFreeMemory = NULL; +#endif + SCardConnectPtr SCardLoader::scardConnect = NULL; + SCardReconnectPtr SCardLoader::scardReconnect = NULL; + SCardDisconnectPtr SCardLoader::scardDisconnect = NULL; + SCardBeginTransactionPtr SCardLoader::scardBeginTransaction = NULL; + SCardEndTransactionPtr SCardLoader::scardEndTransaction = NULL; + SCardStatusPtr SCardLoader::scardStatus = NULL; + SCardGetStatusChangePtr SCardLoader::scardGetStatusChange = NULL; + SCardControlPtr SCardLoader::scardControl = NULL; + SCardTransmitPtr SCardLoader::scardTransmit = NULL; + SCardListReaderGroupsPtr SCardLoader::scardListReaderGroups = NULL; + SCardListReadersPtr SCardLoader::scardListReaders = NULL; + SCardCancelPtr SCardLoader::scardCancel = NULL; + SCardGetAttribPtr SCardLoader::scardGetAttrib = NULL; + SCardSetAttribPtr SCardLoader::scardSetAttrib = NULL; + SCARD_IO_REQUEST* SCardLoader::scardT0Pci = NULL; + SCARD_IO_REQUEST* SCardLoader::scardT1Pci = NULL; + SCARD_IO_REQUEST* SCardLoader::scardRawPci = NULL; + bool SCardLoader::bInitialized = false; + +#ifdef TC_WINDOWS + wstring SCardLoader::GetSCardPath() +#else + string SCardLoader::GetSCardPath() +#endif + { +#ifdef TC_WINDOWS + wchar_t winscardPath[TC_MAX_PATH]; + if (GetSystemDirectory(winscardPath, TC_MAX_PATH)) + { + StringCbCat(winscardPath, sizeof(winscardPath), L"\\Winscard.dll"); + } + else + StringCbCopy(winscardPath, sizeof(winscardPath), L"C:\\Windows\\System32\\Winscard.dll"); + return winscardPath; +#elif TC_MACOSX + return "/System/Library/Frameworks/PCSC.framework/PCSC"; +#else + string pcscPath = ""; + FILE* pipe = +#ifdef TC_LINUX + popen("ldconfig -p", "r"); +#else + popen("ldconfig -r", "r"); // FreeBSD +#endif + if (pipe) + { + char buffer[128]; + while (!feof(pipe)) + { + if (fgets(buffer, 128, pipe) != NULL) + { + string line(buffer); + if (line.find("libpcsclite.so") != string::npos) + { + size_t pos = line.find("=>"); + if (pos != string::npos) + { + pcscPath = line.substr(pos + 3); + pos = pcscPath.find_first_of(" \t\r\n"); + if (pos != string::npos) + pcscPath = pcscPath.substr(0, pos); + break; + } + } + } + } + pclose(pipe); + } + + if (pcscPath == "") + { + pcscPath = "libpcsclite.so"; + } + + return pcscPath; +#endif + } + + void SCardLoader::Initialize() + { + if (bInitialized) + return; + + hScardModule = LoadLibrary(GetSCardPath().c_str()); + if (hScardModule) + { + scardEstablishContext = (SCardEstablishContextPtr)GetProcAddress(hScardModule, "SCardEstablishContext"); + scardReleaseContext = (SCardReleaseContextPtr)GetProcAddress(hScardModule, "SCardReleaseContext"); + scardIsValidContext = (SCardIsValidContextPtr)GetProcAddress(hScardModule, "SCardIsValidContext"); +#ifndef TC_MACOSX + scardFreeMemory = (SCardFreeMemoryPtr)GetProcAddress(hScardModule, "SCardFreeMemory"); +#endif + scardConnect = (SCardConnectPtr)GetProcAddress(hScardModule, SCardConnectName); + scardReconnect = (SCardReconnectPtr)GetProcAddress(hScardModule, "SCardReconnect"); + scardDisconnect = (SCardDisconnectPtr)GetProcAddress(hScardModule, "SCardDisconnect"); + scardBeginTransaction = (SCardBeginTransactionPtr)GetProcAddress(hScardModule, "SCardBeginTransaction"); + scardEndTransaction = (SCardEndTransactionPtr)GetProcAddress(hScardModule, "SCardEndTransaction"); + scardStatus = (SCardStatusPtr)GetProcAddress(hScardModule, SCardStatusName); + scardGetStatusChange = (SCardGetStatusChangePtr)GetProcAddress(hScardModule, SCardGetStatusChangeName); + scardControl = (SCardControlPtr)GetProcAddress(hScardModule, SCardControlName); + scardTransmit = (SCardTransmitPtr)GetProcAddress(hScardModule, "SCardTransmit"); + scardListReaderGroups = (SCardListReaderGroupsPtr)GetProcAddress(hScardModule, SCardListReaderGroupsName); + scardListReaders = (SCardListReadersPtr)GetProcAddress(hScardModule, SCardListReadersName); + scardCancel = (SCardCancelPtr)GetProcAddress(hScardModule, "SCardCancel"); + scardGetAttrib = (SCardGetAttribPtr)GetProcAddress(hScardModule, "SCardGetAttrib"); + scardSetAttrib = (SCardSetAttribPtr)GetProcAddress(hScardModule, "SCardSetAttrib"); + scardT0Pci = (SCARD_IO_REQUEST*)GetProcAddress(hScardModule, "g_rgSCardT0Pci"); + scardT1Pci = (SCARD_IO_REQUEST*)GetProcAddress(hScardModule, "g_rgSCardT1Pci"); + scardRawPci = (SCARD_IO_REQUEST*)GetProcAddress(hScardModule, "g_rgSCardRawPci"); + if ( +#ifndef TC_MACOSX + scardFreeMemory && +#endif + scardEstablishContext && scardReleaseContext && scardIsValidContext && scardConnect && scardReconnect && scardDisconnect && + scardBeginTransaction && scardEndTransaction && scardStatus && scardGetStatusChange && scardControl && scardTransmit && + scardListReaderGroups && scardListReaders && scardCancel && scardGetAttrib && scardSetAttrib && scardT0Pci && scardT1Pci && scardRawPci) + { + if (SCARD_S_SUCCESS == scardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hScardContext)) + { + bInitialized = true; + } + } + } + + if (!bInitialized) + { + Finalize(); + } + } + + void SCardLoader::Finalize() + { + if (hScardContext) + { + scardReleaseContext(hScardContext); + hScardContext = 0; + } + + if (hScardModule) + { + FreeLibrary(hScardModule); + hScardModule = NULL; + } + + scardEstablishContext = NULL; + scardReleaseContext = NULL; + scardIsValidContext = NULL; +#ifndef TC_MACOSX + scardFreeMemory = NULL; +#endif + scardConnect = NULL; + scardReconnect = NULL; + scardDisconnect = NULL; + scardBeginTransaction = NULL; + scardEndTransaction = NULL; + scardStatus = NULL; + scardGetStatusChange = NULL; + scardControl = NULL; + scardTransmit = NULL; + scardListReaderGroups = NULL; + scardListReaders = NULL; + scardCancel = NULL; + scardGetAttrib = NULL; + scardSetAttrib = NULL; + scardT0Pci = NULL; + scardT1Pci = NULL; + scardRawPci = NULL; + + bInitialized = false; + } + + SCARDCONTEXT SCardLoader::GetSCardContext() + { + return hScardContext; + } + + LONG SCardLoader::SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext); + } + + LONG SCardLoader::SCardReleaseContext(SCARDCONTEXT hContext) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardReleaseContext(hContext); + } + + LONG SCardLoader::SCardIsValidContext(SCARDCONTEXT hContext) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardIsValidContext(hContext); + } + +#ifndef TC_MACOSX + LONG SCardLoader::SCardFreeMemory(SCARDCONTEXT hContext, LPCVOID pvMem) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardFreeMemory(hContext, pvMem); + } +#endif + + LONG SCardLoader::SCardConnect(SCARDCONTEXT hContext, LPCTSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardConnect(hContext, szReader, dwShareMode, dwPreferredProtocols, phCard, pdwActiveProtocol); + } + + LONG SCardLoader::SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, DWORD dwPreferredProtocols, DWORD dwInitialization, LPDWORD pdwActiveProtocol) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardReconnect(hCard, dwShareMode, dwPreferredProtocols, dwInitialization, pdwActiveProtocol); + } + + LONG SCardLoader::SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardDisconnect(hCard, dwDisposition); + } + + LONG SCardLoader::SCardBeginTransaction(SCARDHANDLE hCard) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardBeginTransaction(hCard); + } + + LONG SCardLoader::SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardEndTransaction(hCard, dwDisposition); + } + + LONG SCardLoader::SCardStatus(SCARDHANDLE hCard, LPTSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, BYTE* pbAtr, LPDWORD pcbAtrLen) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardStatus(hCard, mszReaderNames, pcchReaderLen, pdwState, pdwProtocol, pbAtr, pcbAtrLen); + } + + LONG SCardLoader::SCardGetStatusChange(SCARDCONTEXT hContext, DWORD dwTimeout, LPSCARD_READERSTATE rgReaderStates, DWORD cReaders) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardGetStatusChange(hContext, dwTimeout, rgReaderStates, cReaders); + } + + LONG SCardLoader::SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID pbSendBuffer, DWORD cbSendLength, LPVOID pbRecvBuffer, DWORD cbRecvLength, LPDWORD lpBytesReturned) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardControl(hCard, dwControlCode, pbSendBuffer, cbSendLength, pbRecvBuffer, cbRecvLength, lpBytesReturned); + } + + LONG SCardLoader::SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, const BYTE* pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, BYTE* pbRecvBuffer, LPDWORD pcbRecvLength) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardTransmit(hCard, pioSendPci, pbSendBuffer, cbSendLength, pioRecvPci, pbRecvBuffer, pcbRecvLength); + } + + LONG SCardLoader::SCardListReaderGroups(SCARDCONTEXT hContext, LPTSTR mszGroups, LPDWORD pcchGroups) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardListReaderGroups(hContext, mszGroups, pcchGroups); + } + + LONG SCardLoader::SCardListReaders(SCARDCONTEXT hContext, LPCTSTR mszGroups, LPTSTR mszReaders, LPDWORD pcchReaders) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardListReaders(hContext, mszGroups, mszReaders, pcchReaders); + } + + LONG SCardLoader::SCardCancel(SCARDCONTEXT hContext) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardCancel(hContext); + } + + LONG SCardLoader::SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, BYTE* pbAttr, LPDWORD pcbAttrLen) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardGetAttrib(hCard, dwAttrId, pbAttr, pcbAttrLen); + } + + LONG SCardLoader::SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, const BYTE* pbAttr, DWORD cbAttrLen) + { + Initialize(); + + if (!bInitialized) + throw ScardLibraryInitializationFailed(); + + return scardSetAttrib(hCard, dwAttrId, pbAttr, cbAttrLen); + } +}
\ No newline at end of file diff --git a/src/Common/SCardLoader.h b/src/Common/SCardLoader.h new file mode 100644 index 00000000..b1ab114b --- /dev/null +++ b/src/Common/SCardLoader.h @@ -0,0 +1,125 @@ +#ifndef TC_HEADER_Common_SCardLoader +#define TC_HEADER_Common_SCardLoader + +#include "Platform/PlatformBase.h" + +#ifdef TC_WINDOWS +#include <winscard.h> +#include <windows.h> +#else +#ifdef TC_MACOSX +#undef BOOL +#include <PCSC/pcsclite.h> +#include <PCSC/winscard.h> +#include <PCSC/wintypes.h> +#include "reader.h" +typedef LPSCARD_READERSTATE_A LPSCARD_READERSTATE; +using VeraCrypt::byte; +#define BOOL int +#else +#undef BOOL +#include "pcsclite.h" +#include <winscard.h> +#include <wintypes.h> +#include <reader.h> +using VeraCrypt::byte; +#define BOOL int +#endif +#endif + +#ifndef TC_WINDOWS +typedef void* HMODULE; +#define SCARD_CALL_SPEC +#else +#define SCARD_CALL_SPEC WINAPI +#endif + +namespace VeraCrypt +{ + typedef LONG (SCARD_CALL_SPEC *SCardEstablishContextPtr)(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext); + typedef LONG (SCARD_CALL_SPEC *SCardReleaseContextPtr)(SCARDCONTEXT hContext); + typedef LONG (SCARD_CALL_SPEC *SCardIsValidContextPtr)(SCARDCONTEXT hContext); +#ifndef TC_MACOSX + typedef LONG (SCARD_CALL_SPEC *SCardFreeMemoryPtr)(SCARDCONTEXT hContext, LPCVOID pvMem); +#endif + typedef LONG (SCARD_CALL_SPEC *SCardConnectPtr)(SCARDCONTEXT hContext, LPCTSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); + typedef LONG (SCARD_CALL_SPEC *SCardReconnectPtr)(SCARDHANDLE hCard, DWORD dwShareMode, DWORD dwPreferredProtocols, DWORD dwInitialization, LPDWORD pdwActiveProtocol); + typedef LONG (SCARD_CALL_SPEC *SCardDisconnectPtr)(SCARDHANDLE hCard, DWORD dwDisposition); + typedef LONG (SCARD_CALL_SPEC *SCardBeginTransactionPtr)(SCARDHANDLE hCard); + typedef LONG (SCARD_CALL_SPEC *SCardEndTransactionPtr)(SCARDHANDLE hCard, DWORD dwDisposition); + typedef LONG (SCARD_CALL_SPEC *SCardStatusPtr)(SCARDHANDLE hCard, LPTSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, BYTE* pbAtr, LPDWORD pcbAtrLen); + typedef LONG (SCARD_CALL_SPEC *SCardGetStatusChangePtr)(SCARDCONTEXT hContext, DWORD dwTimeout, LPSCARD_READERSTATE rgReaderStates, DWORD cReaders); + typedef LONG (SCARD_CALL_SPEC *SCardControlPtr)(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID pbSendBuffer, DWORD cbSendLength, LPVOID pbRecvBuffer, DWORD cbRecvLength, LPDWORD lpBytesReturned); + typedef LONG (SCARD_CALL_SPEC *SCardTransmitPtr)(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, const BYTE* pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, BYTE* pbRecvBuffer, LPDWORD pcbRecvLength); + typedef LONG (SCARD_CALL_SPEC *SCardListReaderGroupsPtr)(SCARDCONTEXT hContext, LPTSTR mszGroups, LPDWORD pcchGroups); + typedef LONG (SCARD_CALL_SPEC *SCardListReadersPtr)(SCARDCONTEXT hContext, LPCTSTR mszGroups, LPTSTR mszReaders, LPDWORD pcchReaders); + typedef LONG (SCARD_CALL_SPEC *SCardCancelPtr)(SCARDCONTEXT hContext); + typedef LONG (SCARD_CALL_SPEC *SCardGetAttribPtr)(SCARDHANDLE hCard, DWORD dwAttrId, BYTE* pbAttr, LPDWORD pcbAttrLen); + typedef LONG (SCARD_CALL_SPEC *SCardSetAttribPtr)(SCARDHANDLE hCard, DWORD dwAttrId, const BYTE* pbAttr, DWORD cbAttrLen); + + class SCardLoader + { + protected: + static HMODULE hScardModule; + static SCARDCONTEXT hScardContext; + static SCardEstablishContextPtr scardEstablishContext; + static SCardReleaseContextPtr scardReleaseContext; + static SCardIsValidContextPtr scardIsValidContext; +#ifndef TC_MACOSX + static SCardFreeMemoryPtr scardFreeMemory; +#endif + static SCardConnectPtr scardConnect; + static SCardReconnectPtr scardReconnect; + static SCardDisconnectPtr scardDisconnect; + static SCardBeginTransactionPtr scardBeginTransaction; + static SCardEndTransactionPtr scardEndTransaction; + static SCardStatusPtr scardStatus; + static SCardGetStatusChangePtr scardGetStatusChange; + static SCardControlPtr scardControl; + static SCardTransmitPtr scardTransmit; + static SCardListReaderGroupsPtr scardListReaderGroups; + static SCardListReadersPtr scardListReaders; + static SCardCancelPtr scardCancel; + static SCardGetAttribPtr scardGetAttrib; + static SCardSetAttribPtr scardSetAttrib; + static bool bInitialized; + + public: + static SCARD_IO_REQUEST* scardT0Pci; + static SCARD_IO_REQUEST* scardT1Pci; + static SCARD_IO_REQUEST* scardRawPci; + + SCardLoader() { }; + static void Initialize(); + static void Finalize(); +#ifdef TC_WINDOWS + static wstring GetSCardPath(); +#else + static string GetSCardPath(); +#endif + static SCARDCONTEXT GetSCardContext(); + + static LONG SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext); + static LONG SCardReleaseContext(SCARDCONTEXT hContext); + static LONG SCardIsValidContext(SCARDCONTEXT hContext); +#ifndef TC_MACOSX + static LONG SCardFreeMemory(SCARDCONTEXT hContext, LPCVOID pvMem); +#endif + static LONG SCardConnect(SCARDCONTEXT hContext, LPCTSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); + static LONG SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, DWORD dwPreferredProtocols, DWORD dwInitialization, LPDWORD pdwActiveProtocol); + static LONG SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition); + static LONG SCardBeginTransaction(SCARDHANDLE hCard); + static LONG SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition); + static LONG SCardStatus(SCARDHANDLE hCard, LPTSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, BYTE* pbAtr, LPDWORD pcbAtrLen); + static LONG SCardGetStatusChange(SCARDCONTEXT hContext, DWORD dwTimeout, LPSCARD_READERSTATE rgReaderStates, DWORD cReaders); + static LONG SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID pbSendBuffer, DWORD cbSendLength, LPVOID pbRecvBuffer, DWORD cbRecvLength, LPDWORD lpBytesReturned); + static LONG SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, const BYTE* pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, BYTE* pbRecvBuffer, LPDWORD pcbRecvLength); + static LONG SCardListReaderGroups(SCARDCONTEXT hContext, LPTSTR mszGroups, LPDWORD pcchGroups); + static LONG SCardListReaders(SCARDCONTEXT hContext, LPCTSTR mszGroups, LPTSTR mszReaders, LPDWORD pcchReaders); + static LONG SCardCancel(SCARDCONTEXT hContext); + static LONG SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, BYTE* pbAttr, LPDWORD pcbAttrLen); + static LONG SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, const BYTE* pbAttr, DWORD cbAttrLen); + }; +}; + +#endif // TC_HEADER_Common_SCardLoader
\ No newline at end of file diff --git a/src/Common/SCardManager.cpp b/src/Common/SCardManager.cpp new file mode 100644 index 00000000..40578d39 --- /dev/null +++ b/src/Common/SCardManager.cpp @@ -0,0 +1,109 @@ +#include "SCardManager.h" +#include "PCSCException.h" + +namespace VeraCrypt +{ + shared_ptr<SCardLoader> SCardManager::loader = make_shared<SCardLoader>(); + + SCardManager::SCardManager() + { + loader->Initialize(); + } + + SCardManager::~SCardManager() + { + loader->Finalize(); + } + + vector<wstring> SCardManager::GetReaders() + { + vector<wstring> readers; + LPTSTR mszReaders = NULL; + LPTSTR ptr = NULL; + DWORD dwReaders = 0; + SCARDCONTEXT hScardContext = 0; + LONG lRet = SCARD_S_SUCCESS; + + hScardContext = loader->GetSCardContext(); + lRet = loader->SCardIsValidContext(hScardContext); + if (SCARD_S_SUCCESS != lRet) + { + loader->SCardReleaseContext(hScardContext); + lRet = loader->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hScardContext); + if (lRet != SCARD_S_SUCCESS) + throw PCSCException(lRet); + } + +#ifdef SCARD_AUTOALLOCATE + dwReaders = SCARD_AUTOALLOCATE; + lRet = loader->SCardListReaders(hScardContext, NULL, (LPTSTR)&mszReaders, &dwReaders); +#else + lRet = loader->SCardListReaders(hScardContext, NULL, NULL, &dwReaders); + if (lRet == SCARD_S_SUCCESS) + { + mszReaders = (LPTSTR)calloc(dwReaders, sizeof(char)); + lRet = loader->SCardListReaders(hScardContext, NULL, mszReaders, &dwReaders); + } +#endif + + if (lRet == SCARD_S_SUCCESS && !mszReaders) + { + lRet = SCARD_E_NO_READERS_AVAILABLE; + } + if (lRet == SCARD_E_NO_READERS_AVAILABLE) + { + readers.clear(); + lRet = SCARD_S_SUCCESS; + } + + if (lRet == SCARD_S_SUCCESS && mszReaders) + { + ptr = mszReaders; + while (*ptr) + { +#ifdef TC_WINDOWS + readers.push_back(ptr); +#else + readers.push_back(StringConverter::ToWide(ptr)); +#endif + ptr += +#ifdef TC_WINDOWS + wcslen(ptr) + 1; +#else + strlen(ptr) + 1; +#endif + } + +#ifdef SCARD_AUTOALLOCATE + loader->SCardFreeMemory(hScardContext, mszReaders); +#else + free(mszReaders); +#endif + } + + if (lRet != SCARD_S_SUCCESS) + throw PCSCException(lRet); + + return readers; + } + + shared_ptr<SCardReader> SCardManager::GetReader(size_t readerNumber) + { + vector<wstring> readers; + shared_ptr<SCardReader> smartCardReader; + + loader->Initialize(); + + readers = GetReaders(); + if (readerNumber < readers.size()) + { + smartCardReader = make_shared<SCardReader>(readers[readerNumber], loader); + if (smartCardReader) + { + return smartCardReader; + } + } + + throw InvalidEMVPath(); + } +}
\ No newline at end of file diff --git a/src/Common/SCardManager.h b/src/Common/SCardManager.h new file mode 100644 index 00000000..94a58cb7 --- /dev/null +++ b/src/Common/SCardManager.h @@ -0,0 +1,21 @@ +#ifndef TC_HEADER_Common_SCardManager +#define TC_HEADER_Common_SCardManager + +#include "Platform/PlatformBase.h" +#include "SCardReader.h" + +namespace VeraCrypt +{ + class SCardManager + { + protected: + static shared_ptr<SCardLoader> loader; + public: + SCardManager(); + virtual ~SCardManager(); + static vector<wstring> GetReaders(); + static shared_ptr<SCardReader> GetReader(size_t readerNumber); + }; +}; + +#endif // TC_HEADER_Common_SCardManager
\ No newline at end of file diff --git a/src/Common/SCardReader.cpp b/src/Common/SCardReader.cpp new file mode 100644 index 00000000..3a762415 --- /dev/null +++ b/src/Common/SCardReader.cpp @@ -0,0 +1,681 @@ +#include "SCardReader.h" +#include "PCSCException.h" + +#include <locale> + +using namespace std; + +namespace VeraCrypt +{ + void SCardReader::Init(const wstring& szSCReaderName, const shared_ptr<SCardLoader> scardLoader, const SCARDHANDLE& hCard, const DWORD& dwProtocol, LPCSCARD_IO_REQUEST pIO_Protocol) + { + m_szSCReaderName = szSCReaderName; + if (scardLoader) + { + m_scardLoader = scardLoader; + m_hSCReaderContext = m_scardLoader->GetSCardContext(); + } + else + { + m_scardLoader = NULL; + m_hSCReaderContext = 0; + } + m_hCard = hCard; + m_dwProtocol = dwProtocol; + m_pIO_Protocol = pIO_Protocol; + } + + SCardReader::SCardReader(const wstring &szName, const shared_ptr<SCardLoader> scardLoader) + { + Init(szName, scardLoader, 0, 0, NULL); + } + + SCardReader::SCardReader(const SCardReader& other) + : m_szSCReaderName(other.m_szSCReaderName), + m_scardLoader(other.m_scardLoader), + m_hSCReaderContext(other.m_hSCReaderContext), + m_hCard(other.m_hCard), + m_dwProtocol(other.m_dwProtocol), + m_pIO_Protocol(other.m_pIO_Protocol) + { + } + + SCardReader::SCardReader(SCardReader&& other) + : m_szSCReaderName(other.m_szSCReaderName), + m_scardLoader(other.m_scardLoader), + m_hSCReaderContext(other.m_hSCReaderContext), + m_hCard(other.m_hCard), + m_dwProtocol(other.m_dwProtocol), + m_pIO_Protocol(other.m_pIO_Protocol) + { + other.Clear(); + } + + SCardReader& SCardReader::operator=(const SCardReader& other) + { + if (this != &other) + { + m_szSCReaderName = other.m_szSCReaderName; + m_scardLoader = other.m_scardLoader; + m_hSCReaderContext = other.m_hSCReaderContext; + m_hCard = other.m_hCard; + m_dwProtocol = other.m_dwProtocol; + m_pIO_Protocol = other.m_pIO_Protocol; + } + return *this; + } + + SCardReader& SCardReader::operator=(SCardReader&& other) + { + if (this != &other) + { + m_szSCReaderName = other.m_szSCReaderName; + m_scardLoader = other.m_scardLoader; + m_hSCReaderContext = other.m_hSCReaderContext; + m_hCard = other.m_hCard; + m_dwProtocol = other.m_dwProtocol; + m_pIO_Protocol = other.m_pIO_Protocol; + + other.Clear(); + } + return *this; + } + + void SCardReader::Clear(void) + { + m_szSCReaderName = L""; + m_scardLoader = NULL; + m_hSCReaderContext = 0; + m_hCard = 0; + m_dwProtocol = 0; + m_pIO_Protocol = NULL; + } + + SCardReader::~SCardReader() + { + Clear(); + } + + const wstring SCardReader::GetNameWide() const + { + return m_szSCReaderName; + } + + const string SCardReader::GetName() const + { + string name = ""; + size_t size = wcstombs(NULL, m_szSCReaderName.c_str(), 0) + 1; + if (size) + { + name.resize(size); + size = wcstombs(&name[0], m_szSCReaderName.c_str(), size); + if (size) + { + name.resize(size); + } + } + return name; + } + + bool SCardReader::IsCardPresent(vector<byte>& cardAtr) + { + LONG lRet = SCARD_S_SUCCESS; + SCARD_READERSTATE state; + bool bIsCardPresent = false; +#ifdef TC_WINDOWS + wstring readerName = GetNameWide(); +#else + string readerName = GetName(); +#endif + + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + cardAtr.clear(); + burn(&state, sizeof(SCARD_READERSTATE)); + state.szReader = readerName.c_str(); + + lRet = m_scardLoader->SCardIsValidContext(m_hSCReaderContext); + if (SCARD_S_SUCCESS != lRet) + { + m_scardLoader->SCardReleaseContext(m_hSCReaderContext); + lRet = m_scardLoader->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &m_hSCReaderContext); + if (lRet != SCARD_S_SUCCESS) + throw PCSCException(lRet); + } + + lRet = m_scardLoader->SCardGetStatusChange(m_hSCReaderContext, 0, &state, 1); + if (lRet == SCARD_S_SUCCESS) + { + if ((state.dwEventState & SCARD_STATE_PRESENT) == SCARD_STATE_PRESENT && (state.dwEventState & SCARD_STATE_MUTE) == 0) + { + cardAtr.resize(state.cbAtr, 0); + memcpy(cardAtr.data(), state.rgbAtr, state.cbAtr); + bIsCardPresent = true; + burn(&state, sizeof(SCARD_READERSTATE)); + } + } + else + { + throw PCSCException(lRet); + } + + return bIsCardPresent; + } + + bool SCardReader::IsCardPresent() + { + vector<byte> dummy; + return IsCardPresent(dummy); + } + + LONG SCardReader::CardHandleStatus() + { + LONG lRet = SCARD_E_INVALID_HANDLE; + + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + if (m_hCard != 0) + { +#ifdef TC_WINDOWS + wchar_t +#else + char +#endif + szName[TC_MAX_PATH] = {}; + BYTE pbAtr[36] = {}; + DWORD dwState, dwProtocol, dwNameLen = TC_MAX_PATH, dwAtrLen = 36; + lRet = m_scardLoader->SCardStatus(m_hCard, szName, &dwNameLen, &dwState, &dwProtocol, pbAtr, &dwAtrLen); + } + + return lRet; + } + + void SCardReader::Connect(DWORD dwProtocolToUse, bool& bHasBeenReset, bool resetAfterConnect) + { + LONG lRet = SCARD_S_SUCCESS; + bHasBeenReset = false; +#ifdef TC_WINDOWS + wstring readerName = GetNameWide(); +#else + string readerName = GetName(); +#endif + + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + lRet = m_scardLoader->SCardIsValidContext(m_hSCReaderContext); + if (SCARD_S_SUCCESS != lRet) + { + m_scardLoader->SCardReleaseContext(m_hSCReaderContext); + lRet = m_scardLoader->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &m_hSCReaderContext); + if (lRet != SCARD_S_SUCCESS) + throw PCSCException(lRet); + } + + if (m_hCard != 0) + { + lRet = CardHandleStatus(); + if (lRet == SCARD_W_RESET_CARD) + { + bHasBeenReset = true; + lRet = m_scardLoader->SCardReconnect( + m_hCard, + SCARD_SHARE_SHARED, + dwProtocolToUse, + SCARD_LEAVE_CARD, + &m_dwProtocol); + if (lRet != SCARD_S_SUCCESS) + { + throw PCSCException(lRet); + } + } + else if (lRet != SCARD_S_SUCCESS) + { + // Card handle is invalid, disconnect and reconnect. + Disconnect(); + } + } + + if (m_hCard == 0) + { + lRet = m_scardLoader->SCardConnect( + m_hSCReaderContext, + readerName.c_str(), + SCARD_SHARE_SHARED, + dwProtocolToUse, + &m_hCard, + &m_dwProtocol); + if (lRet != SCARD_S_SUCCESS) + { + throw PCSCException(lRet); + } + } + + if (m_pIO_Protocol == NULL) + { + if (m_dwProtocol == SCARD_PROTOCOL_T0) + { + m_pIO_Protocol = m_scardLoader->scardT0Pci; + } + else if (m_dwProtocol == SCARD_PROTOCOL_T1) + { + m_pIO_Protocol = m_scardLoader->scardT1Pci; + } + else if (m_dwProtocol == SCARD_PROTOCOL_RAW) + { + m_pIO_Protocol = m_scardLoader->scardRawPci; + } + else + { + lRet = SCARD_E_INVALID_PARAMETER; + Disconnect(); + throw PCSCException(lRet); + } + } + + if (resetAfterConnect) + { + lRet = m_scardLoader->SCardReconnect( + m_hCard, + SCARD_SHARE_SHARED, + m_dwProtocol, + SCARD_RESET_CARD, + &m_dwProtocol); + + if (lRet != SCARD_S_SUCCESS) + { + Disconnect(); + throw PCSCException(lRet); + } + } + } + + bool SCardReader::IsConnected() + { + return m_hCard != 0; + } + + void SCardReader::Disconnect() const + { + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + if (m_hCard != 0) + { + m_scardLoader->SCardDisconnect(m_hCard, SCARD_LEAVE_CARD); + m_dwProtocol = 0; + m_hCard = 0; + m_pIO_Protocol = NULL; + } + } + + LONG SCardReader::SendAPDU(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength, uint16& SW) const + { + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + LONG lRet = m_scardLoader->SCardTransmit(m_hCard, m_pIO_Protocol, pbSendBuffer, cbSendLength, NULL, pbRecvBuffer, pcbRecvLength); + + if (SCARD_S_SUCCESS == lRet) + { + if (*pcbRecvLength < 2) // must be at least = 2 (SW) + { + lRet = SCARD_E_UNEXPECTED; + } + else + { + SW = (pbRecvBuffer[*pcbRecvLength - 2] << 8) | pbRecvBuffer[*pcbRecvLength - 1]; + *pcbRecvLength -= 2; + } + } + + return lRet; + } + + void SCardReader::BeginTransaction() + { + LONG lRet = 0; + + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + if (m_hCard != 0) + { +#ifndef _DEBUG + lRet = m_scardLoader->SCardBeginTransaction(m_hCard); + if (lRet != SCARD_S_SUCCESS) + { + throw PCSCException(lRet); + } +#else + lRet = SCARD_S_SUCCESS; +#endif + } + else + { + lRet = SCARD_E_INVALID_HANDLE; + throw PCSCException(lRet); + } + } + + void SCardReader::EndTransaction() + { + LONG lRet = 0; + + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + if (m_hCard != 0) + { +#ifndef _DEBUG + lRet = m_scardLoader->SCardEndTransaction(m_hCard, SCARD_LEAVE_CARD); + if (lRet != SCARD_S_SUCCESS) + { + throw PCSCException(lRet); + } +#endif + lRet = SCARD_S_SUCCESS; + } + else + { + lRet = SCARD_E_INVALID_HANDLE; + throw PCSCException(lRet); + } + } + + void SCardReader::ApduProcessData(CommandAPDU commandAPDU, ResponseAPDU& responseAPDU) const + { + LONG lRet = 0; + uint16 SW = 0; + + uint32 nc = 0, ne = 0; + + bool expectingResponse = false; + bool useExtendedAPDU = false; + + size_t indexOfLe = 0; + size_t indexOfLcData = 0; + + vector<byte> pbSendBuffer; + vector<byte> pbRecvBuffer; + DWORD cbSendLength = 0; + DWORD cbRecvLength = 0; + + responseAPDU.clear(); + + if (!commandAPDU.isValid()) + { + throw CommandAPDUNotValid(SRC_POS, commandAPDU.getErrorStr()); + } + + // See whether the CommandAPDU is extended or not + useExtendedAPDU = commandAPDU.isExtended(); + + // If T != 1, cannot use Extended-APDU + if (m_dwProtocol != SCARD_PROTOCOL_T1 && useExtendedAPDU) + { + throw ExtendedAPDUNotSupported(); + } + + // Set some needed vars + nc = commandAPDU.getNc(); + ne = commandAPDU.getNe(); + pbSendBuffer.resize(useExtendedAPDU ? extendedAPDUMaxSendSize : shortAPDUMaxSendSize, 0); + pbRecvBuffer.resize(useExtendedAPDU ? extendedAPDUMaxRecvSize : shortAPDUMaxRecvSize, 0); + cbRecvLength = (DWORD)pbRecvBuffer.size(); + + if (nc > (useExtendedAPDU ? extendedAPDUMaxTransSize : shortAPDUMaxTransSize) - 1) // Max = 255 or 65535 + { + std::string errStr = vformat("Nc > %d", (useExtendedAPDU ? extendedAPDUMaxTransSize : shortAPDUMaxTransSize) - 1); + throw CommandAPDUNotValid(SRC_POS, commandAPDU.getErrorStr()); + } + if (ne > (useExtendedAPDU ? extendedAPDUMaxTransSize : shortAPDUMaxTransSize)) // Max = 256 or 65536 + { + std::string errStr = vformat("Ne > %d", (useExtendedAPDU ? extendedAPDUMaxTransSize : shortAPDUMaxTransSize) - 1); + throw CommandAPDUNotValid(SRC_POS, commandAPDU.getErrorStr()); + } + + // Create and populate buffer to send to card + pbSendBuffer[0] = commandAPDU.getCLA(); + pbSendBuffer[1] = commandAPDU.getINS(); + pbSendBuffer[2] = commandAPDU.getP1(); + pbSendBuffer[3] = commandAPDU.getP2(); + if (nc == 0) + { + if (ne == 0) + { + // case 1 + cbSendLength = 4; + } + else + { + expectingResponse = true; + + // case 2s or 2e + if (ne <= 256) + { + // case 2s + // 256 is encoded as 0x00 + pbSendBuffer[4] = (BYTE)ne; + indexOfLe = 4; + cbSendLength = 4 + 1; // header || Le (1 byte) + } + else + { + // case 2e + // 65536 is encoded as 0x00 0x00 0x00 + BYTE l1, l2; + if (ne == 65536) + { + l1 = 0; + l2 = 0; + } + else + { + l1 = (BYTE)(ne >> 8); + l2 = (BYTE)ne; + } + pbSendBuffer[4] = 0x00; + pbSendBuffer[5] = l1; + pbSendBuffer[6] = l2; + cbSendLength = 4 + 3; // header || Le (3 bytes) + } + } + } + else + { + if (ne == 0) + { + // case 3s or 3e + if (nc <= 255) + { + // case 3s + pbSendBuffer[4] = (BYTE)nc; + indexOfLcData = 5; + cbSendLength = 4 + 1 + nc; // header || Lc (1 byte) || Data + memcpy(&pbSendBuffer[indexOfLcData], commandAPDU.getData().data(), nc); + } + else + { + // case 3e + pbSendBuffer[4] = 0; + pbSendBuffer[5] = (BYTE)(nc >> 8); + pbSendBuffer[6] = (BYTE)nc; + indexOfLcData = 7; + cbSendLength = 4 + 3 + nc; // header || Lc (3 bytes) || Data + memcpy(&pbSendBuffer[indexOfLcData], commandAPDU.getData().data(), nc); + } + } + else + { + expectingResponse = true; + + // case 4s or 4e + if ((nc <= 255) && (ne <= 256)) + { + // case 4s + pbSendBuffer[4] = (BYTE)nc; + indexOfLcData = 5; + cbSendLength = 4 + 1 + nc + 1; // header || Lc (1 byte) || Data || Le (1 byte) + memcpy(&pbSendBuffer[indexOfLcData], commandAPDU.getData().data(), nc); + pbSendBuffer[indexOfLcData + nc] = (ne != 256) ? (BYTE)ne : 0; + indexOfLe = indexOfLcData + nc; + } + else + { + // case 4e + pbSendBuffer[4] = 0; + pbSendBuffer[5] = (BYTE)(nc >> 8); + pbSendBuffer[6] = (BYTE)nc; + indexOfLcData = 7; + cbSendLength = 4 + 3 + nc + 2; // header || Lc (3 bytes) || Data || Le (2 bytes) + memcpy(&pbSendBuffer[indexOfLcData], commandAPDU.getData().data(), nc); + if (ne != 65536) + { + size_t leOfs = cbSendLength - 2; + pbSendBuffer[leOfs] = (BYTE)(ne >> 8); + pbSendBuffer[leOfs + 1] = (BYTE)ne; + }// 65536 is 0x00 0x00 and the buffer has already been initialized with 0s + } + } + } + cbRecvLength = (DWORD)pbRecvBuffer.size(); + lRet = SendAPDU(pbSendBuffer.data(), cbSendLength, pbRecvBuffer.data(), &cbRecvLength, SW); + if (lRet != SCARD_S_SUCCESS) + { + responseAPDU.setSW(SW); + goto end; + } + + // If Expecting Response + if (expectingResponse) + { + // If Short-APDU + if (!useExtendedAPDU) + { + // If SW != 0x9000 + if (SW != SW_NO_ERROR) + { + // If SW == 0x6CXX => Le larger than actual available data on ICC, SW2 contains the appropriate value + if ((BYTE)(SW >> 8) == (BYTE)(SW_CORRECT_LENGTH_00 >> 8)) // 0x6C + { + pbSendBuffer[indexOfLe] = (BYTE)(SW & 0x00FF); + cbRecvLength = (DWORD)pbRecvBuffer.size(); + lRet = SendAPDU(pbSendBuffer.data(), cbSendLength, pbRecvBuffer.data(), &cbRecvLength, SW); + + if (lRet != SCARD_S_SUCCESS) + { + responseAPDU.setSW(SW); + goto end; + } + } + + // If SW != 0x61XX (GET RESPONSE REMAINING BYTES) => there was an unexpected error + if (SW != SW_NO_ERROR && ((BYTE)(SW >> 8) != (BYTE)(SW_BYTES_REMAINING_00 >> 8))) // 0x61 + { + responseAPDU.setSW(SW); + goto end; + } + } + + // Get response data from APDU Response + // Response might be complete (1 APDU, <= 256 bytes : SW = 0x9000) or needs a Get Response to get the rest (1st APDU, == 256 bytes, SW = 0x61XX) + if (cbRecvLength) + responseAPDU.appendData(pbRecvBuffer.data(), cbRecvLength); + + // Send get response to get the rest as long as we receive SW == 0x61XX + // In case of PACE, this is never the case + while ((lRet == SCARD_S_SUCCESS) && ((BYTE)(SW >> 8) == (BYTE)(SW_BYTES_REMAINING_00 >> 8))) // 0x61 + { + // GET RESPONSE APDU + pbSendBuffer[0] = commandAPDU.getCLA(); + pbSendBuffer[1] = INS_GET_RESPONSE; + pbSendBuffer[2] = 0x00; + pbSendBuffer[3] = 0x00; + pbSendBuffer[4] = (BYTE)(SW & 0x00FF); + cbSendLength = 5; + + cbRecvLength = (DWORD)pbRecvBuffer.size(); + lRet = SendAPDU(pbSendBuffer.data(), cbSendLength, pbRecvBuffer.data(), &cbRecvLength, SW); + + if (lRet == SCARD_S_SUCCESS) + { + if ((SW != SW_NO_ERROR) && ((SW >> 8) != (BYTE)(SW_BYTES_REMAINING_00 >> 8))) // 0x61 + { + responseAPDU.clear(); + responseAPDU.setSW(SW); + } + else + responseAPDU.appendData(pbRecvBuffer.data(), cbRecvLength); + } + } + } + // If Extended-APDU (SW = 0x6CXX and SW = 0x61XX are handled by the low-level driver + smart card reader) + else + { + // If SW != 0x9000 => there was an unexpected error + if (SW != SW_NO_ERROR) + { + responseAPDU.setSW(SW); + goto end; + } + + // Response is complete in 1 ResponseAPDU + if (cbRecvLength) + responseAPDU.appendData(pbRecvBuffer.data(), cbRecvLength); + } + + if (lRet == SCARD_S_SUCCESS) + { + responseAPDU.setSW(SW); + } + } + else + { + responseAPDU.setSW(SW); + } + + end: + + burn(pbSendBuffer.data(), pbSendBuffer.size()); + burn(pbRecvBuffer.data(), pbRecvBuffer.size()); + + if (lRet != SCARD_S_SUCCESS) + throw PCSCException(lRet); + } + + void SCardReader::GetATRFromHandle(vector<byte>& atrValue) + { + vector<byte> pbATR; + DWORD cByte = 0; + LONG lRet = 0; + + atrValue.clear(); + + if (!m_scardLoader) + throw ScardLibraryInitializationFailed(); + + lRet = m_scardLoader->SCardGetAttrib(m_hCard, SCARD_ATTR_ATR_STRING, NULL, &cByte); + if (lRet == SCARD_S_SUCCESS) + { + pbATR.resize(cByte, 0); + lRet = m_scardLoader->SCardGetAttrib(m_hCard, SCARD_ATTR_ATR_STRING, pbATR.data(), &cByte); + + if (lRet == SCARD_S_SUCCESS) + { + atrValue = pbATR; + } + else + { + throw PCSCException(lRet); + } + } + else + { + throw PCSCException(lRet); + } + } +} + diff --git a/src/Common/SCardReader.h b/src/Common/SCardReader.h new file mode 100644 index 00000000..95586963 --- /dev/null +++ b/src/Common/SCardReader.h @@ -0,0 +1,200 @@ +#ifndef TC_HEADER_Common_SCardReader +#define TC_HEADER_Common_SCardReader + +#include "Platform/PlatformBase.h" +#include "CommandAPDU.h" +#include "ResponseAPDU.h" +#include "SCardLoader.h" + +namespace VeraCrypt +{ + /* ================================================================================================ */ + /* SW values */ + /* ================================================================================================ */ + const uint16 SW_BYTES_REMAINING_00 = (uint16)0x6100; + const uint16 SW_STATE_NON_VOLATILE_MEMORY_UNCHANGED_NO_INFORMATION_GIVEN = (uint16)0x6200; + const uint16 SW_END_OF_FILE = (uint16)0x6282; + const uint16 SW_LESS_DATA_RESPONDED_THAN_REQUESTED = (uint16)0x6287; + const uint16 SW_NON_VOLATILE_MEMORY_CHANGED_NO_INFORMATION_GIVEN = (uint16)0x6300; + const uint16 SW_NON_VOLATILE_MEMORY_CHANGED_FILE_FILLED_UP_BY_LAST_WRITE = (uint16)0x6381; + const uint16 SW_NON_VOLATILE_MEMORY_CHANGED_COUNTER_0 = (uint16)0x63C0; + const uint16 SW_WRONG_LENGTH = (uint16)0x6700; + const uint16 SW_LOGICAL_CHANNEL_NOT_SUPPORTED = (uint16)0x6881; + const uint16 SW_SECURE_MESSAGING_NOT_SUPPORTED = (uint16)0x6882; + const uint16 SW_LAST_COMMAND_EXPECTED = (uint16)0x6883; + const uint16 SW_SECURITY_STATUS_NOT_SATISFIED = (uint16)0x6982; + const uint16 SW_FILE_INVALID = (uint16)0x6983; + const uint16 SW_DATA_INVALID = (uint16)0x6984; + const uint16 SW_CONDITIONS_NOT_SATISFIED = (uint16)0x6985; + const uint16 SW_COMMAND_NOT_ALLOWED = (uint16)0x6986; + const uint16 SW_EXPECTED_SM_DATA_OBJECTS_MISSING = (uint16)0x6987; + const uint16 SW_SM_DATA_OBJECTS_INCORRECT = (uint16)0x6988; + const uint16 SW_APPLET_SELECT_FAILED = (uint16)0x6999; + const uint16 SW_KEY_USAGE_ERROR = (uint16)0x69C1; + const uint16 SW_WRONG_DATA = (uint16)0x6A80; + const uint16 SW_FILEHEADER_INCONSISTENT = (uint16)0x6A80; + const uint16 SW_FUNC_NOT_SUPPORTED = (uint16)0x6A81; + const uint16 SW_FILE_NOT_FOUND = (uint16)0x6A82; + const uint16 SW_RECORD_NOT_FOUND = (uint16)0x6A83; + const uint16 SW_FILE_FULL = (uint16)0x6A84; + const uint16 SW_OUT_OF_MEMORY = (uint16)0x6A84; + const uint16 SW_INCORRECT_P1P2 = (uint16)0x6A86; + const uint16 SW_KEY_NOT_FOUND = (uint16)0x6A88; + const uint16 SW_WRONG_P1P2 = (uint16)0x6B00; + const uint16 SW_CORRECT_LENGTH_00 = (uint16)0x6C00; + const uint16 SW_INS_NOT_SUPPORTED = (uint16)0x6D00; + const uint16 SW_CLA_NOT_SUPPORTED = (uint16)0x6E00; + const uint16 SW_UNKNOWN = (uint16)0x6F00; + const uint16 SW_CARD_TERMINATED = (uint16)0x6FFF; + const uint16 SW_NO_ERROR = (uint16)0x9000; + + /* ================================================================================================ */ + /* CLA values */ + /* ================================================================================================ */ + const byte CLA_ISO7816 = (byte)0x00; + const byte CLA_COMMAND_CHAINING = (byte)0x10; + + /* ================================================================================================ */ + /* INS values */ + /* ================================================================================================ */ + const byte INS_ERASE_BINARY = 0x0E; + const byte INS_VERIFY = 0x20; + const byte INS_CHANGE_CHV = 0x24; + const byte INS_UNBLOCK_CHV = 0x2C; + const byte INS_DECREASE = 0x30; + const byte INS_INCREASE = 0x32; + const byte INS_DECREASE_STAMPED = 0x34; + const byte INS_REHABILITATE_CHV = 0x44; + const byte INS_MANAGE_CHANNEL = 0x70; + const byte INS_EXTERNAL_AUTHENTICATE = (byte)0x82; + const byte INS_MUTUAL_AUTHENTICATE = (byte)0x82; + const byte INS_GET_CHALLENGE = (byte)0x84; + const byte INS_ASK_RANDOM = (byte)0x84; + const byte INS_GIVE_RANDOM = (byte)0x86; + const byte INS_INTERNAL_AUTHENTICATE = (byte)0x88; + const byte INS_SEEK = (byte)0xA2; + const byte INS_SELECT = (byte)0xA4; + const byte INS_SELECT_FILE = (byte)0xA4; + const byte INS_CLOSE_APPLICATION = (byte)0xAC; + const byte INS_READ_BINARY = (byte)0xB0; + const byte INS_READ_BINARY2 = (byte)0xB1; + const byte INS_READ_RECORD = (byte)0xB2; + const byte INS_READ_RECORD2 = (byte)0xB3; + const byte INS_READ_RECORDS = (byte)0xB2; + const byte INS_READ_BINARY_STAMPED = (byte)0xB4; + const byte INS_READ_RECORD_STAMPED = (byte)0xB6; + const byte INS_GET_RESPONSE = (byte)0xC0; + const byte INS_ENVELOPE = (byte)0xC2; + const byte INS_GET_DATA = (byte)0xCA; + const byte INS_WRITE_BINARY = (byte)0xD0; + const byte INS_WRITE_RECORD = (byte)0xD2; + const byte INS_UPDATE_BINARY = (byte)0xD6; + const byte INS_LOAD_KEY_FILE = (byte)0xD8; + const byte INS_PUT_DATA = (byte)0xDA; + const byte INS_UPDATE_RECORD = (byte)0xDC; + const byte INS_CREATE_FILE = (byte)0xE0; + const byte INS_APPEND_RECORD = (byte)0xE2; + const byte INS_DELETE_FILE = (byte)0xE4; + const byte INS_PSO = (byte)0x2A; + const byte INS_MSE = (byte)0x22; + + /* ================================================================================================ */ + /* EMV values */ + /* ================================================================================================ */ + const uint16 EMV_CPLC_TAG = (uint16)0x9F7F; + const uint16 EMV_ICC_PK_CERT_TAG = (uint16)0x9F46; + const uint16 EMV_FCI_ISSUER_DISCRETIONARY_DATA_TAG = (uint16)0xBF0C; + const byte EMV_ISS_PK_CERT_TAG = (byte)0x90; + const byte EMV_PAN_TAG = (byte)0x5A; + const byte EMV_FCI_TAG = (byte)0x6F; + const byte EMV_DFNAME_TAG = (byte)0x84; + const byte EMV_FCI_ISSUER_TAG = (byte)0xA5; + const byte EMV_DIRECTORY_ENTRY_TAG = (byte)0x61; + const byte EMV_SFI_TAG = (byte)0x88; + const byte EMV_TEMPLATE_TAG = (byte)0x70; + const byte EMV_AID_TAG = (byte)0x4F; + const byte EMV_LABEL_TAG = (byte)0x50; + const byte EMV_PRIORITY_TAG = (byte)0x87; + const byte EMV_PSE1[] = { 0x31, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31 }; // "1PAY.SYS.DDF01" (contact) + const byte EMV_PSE2[] = { 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31 }; // "2PAY.SYS.DDF01" (contactless) + + /* ================================================================================================ */ + + class SCardReader + { + protected: + + wstring m_szSCReaderName; + + shared_ptr<SCardLoader> m_scardLoader; + mutable SCARDCONTEXT m_hSCReaderContext; + mutable SCARDHANDLE m_hCard; + mutable DWORD m_dwProtocol; + mutable LPCSCARD_IO_REQUEST m_pIO_Protocol; + + void Init(const wstring& szSCReaderName, const shared_ptr<SCardLoader> scardLoader, const SCARDHANDLE& hCard, const DWORD& dwProtocol, LPCSCARD_IO_REQUEST pIO_Protocol); + + public: + + /* Card variables */ + // Max Command APDU total size ; Typically either 261 (short) or 65544 (extended) + // Max Response APDU total size ; Typically either 258 (short) or 65538 (extended) + // Max Response APDU data size ; Ne ; Typically either 256 (short : 0x00) of 65536 (extended : 0x0000) + const static uint32 shortAPDUMaxSendSize = 261; + const static uint32 shortAPDUMaxRecvSize = 258; + const static uint32 shortAPDUMaxTransSize = 256; + const static uint32 extendedAPDUMaxSendSize = 65544; + const static uint32 extendedAPDUMaxRecvSize = 65538; + const static uint32 extendedAPDUMaxTransSize = 65536; + + // ------------------------------------------------------------------------------------------------------------------------------------- // + // Ctors, dtors + // ------------------------------------------------------------------------------------------------------------------------------------- // + + SCardReader(const wstring &szName, const shared_ptr<SCardLoader> scardLoader); + + SCardReader(const SCardReader& other); + SCardReader(SCardReader&& other); + SCardReader& operator = (const SCardReader& other); + SCardReader& operator = (SCardReader&& other); + + void Clear(void); + + ~SCardReader(); + + // ------------------------------------------------------------------------------------------------------------------------------------- // + // Getters & Setters + // ------------------------------------------------------------------------------------------------------------------------------------- // + + const wstring GetNameWide() const; + const string GetName() const; + + // ------------------------------------------------------------------------------------------------------------------------------------- // + // Card Connection management methods + // ------------------------------------------------------------------------------------------------------------------------------------- // + + bool IsCardPresent(vector<byte>& cardAtr); + bool IsCardPresent(); + + LONG CardHandleStatus(); + + void Connect(DWORD dwProtocolToUse, bool& bHasBeenReset, bool resetAfterConnect = false); + bool IsConnected(); + void Disconnect() const; + + LONG SendAPDU(LPCBYTE pbSendBuffer, + DWORD cbSendLength, + LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength, + uint16& SW) const; + + void BeginTransaction(); + void EndTransaction(); + + void ApduProcessData(CommandAPDU commandAPDU, ResponseAPDU& responseAPDU) const; + + void GetATRFromHandle(vector<byte>& atrValue); + }; +}; + +#endif // TC_HEADER_Common_SCardReader
\ No newline at end of file diff --git a/src/Common/SecurityToken.h b/src/Common/SecurityToken.h index 0d671dcc..4dff42fa 100644 --- a/src/Common/SecurityToken.h +++ b/src/Common/SecurityToken.h @@ -59,6 +59,7 @@ namespace VeraCrypt { struct SecurityTokenInfo: TokenInfo { + virtual ~SecurityTokenInfo() {}; virtual BOOL isEditable() const {return true;} CK_FLAGS Flags; @@ -71,10 +72,13 @@ namespace VeraCrypt SecurityTokenKeyfile(const TokenKeyfilePath& path); + virtual ~SecurityTokenKeyfile() {} + operator TokenKeyfilePath () const; void GetKeyfileData(vector<byte>& keyfileData) const; + string IdUtf8; CK_OBJECT_HANDLE Handle; }; diff --git a/src/Common/TLVParser.cpp b/src/Common/TLVParser.cpp index bda9dec0..9a177007 100644 --- a/src/Common/TLVParser.cpp +++ b/src/Common/TLVParser.cpp @@ -3,177 +3,185 @@ using namespace std; -/* TLV node structure creation */ -shared_ptr<TLVNode> TLVParser::TLV_CreateNode() +namespace VeraCrypt { - shared_ptr<TLVNode> node= shared_ptr<TLVNode>(new TLVNode); - memset(node.get(),0,sizeof(*node)); - return node; -} + /* TLV node structure creation */ + shared_ptr<TLVNode> TLVParser::TLV_CreateNode() + { + shared_ptr<TLVNode> node = shared_ptr<TLVNode>(new TLVNode()); + return node; + } -/* Check if the bit is correct */ -uint16_t TLVParser::CheckBit(unsigned char value, int bit){ - unsigned char bitvalue[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + /* Check if the bit is correct */ + uint16 TLVParser::CheckBit(byte value, int bit) + { + unsigned char bitvalue[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; - if((bit >= 1)&&(bit <= 8)){ - if(value & bitvalue[bit-1]) { - return (1); + if ((bit >= 1) && (bit <= 8)) + { + if (value & bitvalue[bit-1]) + { + return (1); + } + else + { + return (0); + } } - else { - return (0); + else + { + throw TLVException("FILE:"+string(__FILE__)+"LINE: "+to_string(static_cast<long long>((__LINE__)))+" fonction parameter incorrect! bit=["+to_string(static_cast<long long>(bit))); + //return(2); } } - else{ - throw TLVException("FILE:"+string(__FILE__)+"LINE: "+to_string(static_cast<long long>((__LINE__)))+" fonction parameter incorrect! bit=["+to_string(static_cast<long long>(bit))); - return(2); - } -} - -/* Parsing one TLV node */ -shared_ptr<TLVNode> TLVParser::TLV_Parse_One(unsigned char* buf,int size){ - int index = 0; - int i; - unsigned char tag1,tag2,tagsize; - unsigned char len,lensize; - unsigned char* value; - shared_ptr<TLVNode> node = TLV_CreateNode(); - - tag1 = tag2 = 0; - tagsize = 1; - tag1 = buf[index++]; - if((tag1 & 0x1f) == 0x1f){ - tagsize++; - tag2 = buf[index++]; - //tag2 b8 must be 0! - } - if(tagsize == 1) { - node->Tag = tag1; - } - else { - node->Tag = (tag1 << 8) + tag2; - } - node->TagSize = tagsize; - //SubFlag - node->SubFlag = CheckBit(tag1,6); + /* Parsing one TLV node */ + shared_ptr<TLVNode> TLVParser::TLV_Parse_One(byte* buf, size_t size) + { + size_t index = 0; + size_t i = 0; + byte tag1, tag2, tagsize; + byte len, lensize; + shared_ptr<vector<byte>> value = make_shared<vector<byte>>(); + shared_ptr<TLVNode> node = TLV_CreateNode(); + + tag1 = tag2 = 0; + tagsize = 1; + tag1 = buf[index++]; + if ((tag1 & 0x1f) == 0x1f) + { + tagsize++; + tag2 = buf[index++]; + //tag2 b8 must be 0! + } + if (tagsize == 1) + { + node->Tag = tag1; + } + else + { + node->Tag = (tag1 << 8) + tag2; + } + node->TagSize = tagsize; - //L zone - len = 0; - lensize = 1; - len = buf[index++]; - if(CheckBit(len,8) == 0){ - node->Length = len; - } - else{ - lensize = len & 0x7f; + //SubFlag + node->SubFlag = CheckBit(tag1,6); + + //L zone len = 0; - for(i=0;i<lensize;i++){ - len += (uint16_t)buf[index++] << (i*8); + lensize = 1; + len = buf[index++]; + if (CheckBit(len,8) == 0) + { + node->Length = len; } - lensize++; - } - node->Length = len; - node->LengthSize = lensize; + else + { + lensize = len & 0x7f; + len = 0; + for (i = 0; i < lensize; i++) + { + len += (uint16)buf[index++] << (i*8); + } + lensize++; + } + node->Length = len; + node->LengthSize = lensize; - //V zone - value = new unsigned char[len]; - std::copy(buf+index,buf+index+len,value); - node->Value = value; - index += len; + //V zone + value->resize(len); + memcpy(value->data(), buf + index, len); + node->Value = value; + index += len; - if(index < size){ - node->MoreFlag = 1; - } - else if(index == size){ - node->MoreFlag = 0; - } - else{ - throw TLVException("Parse Error! index="+to_string(static_cast<long long>(index))+"size="+to_string(static_cast<long long>(size))); - } + if (index < size) + { + node->MoreFlag = 1; + } + else if(index == size) + { + node->MoreFlag = 0; + } + else + { + throw TLVException("Parse Error! index="+to_string(static_cast<long long>(index))+"size="+to_string(static_cast<long long>(size))); + } - return node; -} + return node; + } -/* Parsing all sub-nodes (in width not in depth) of a given parent node */ -int TLVParser::TLV_Parse_SubNodes(shared_ptr<TLVNode> parent){ - int sublen = 0; - int i; + /* Parsing all sub-nodes (in width not in depth) of a given parent node */ + int TLVParser::TLV_Parse_SubNodes(shared_ptr<TLVNode> parent) + { + uint16 sublen = 0; + size_t i; - //No sub-nodes - if(parent->SubFlag == 0) - return 0; + //No sub-nodes + if (parent->SubFlag == 0) + return 0; - for(i=0;i<parent->SubCount;i++) - { - sublen += (parent->Sub[i]->TagSize + parent->Sub[i]->Length + parent->Sub[i]->LengthSize); - } + for (i = 0; i < parent->Subs->size(); i++) + { + sublen += (parent->Subs->at(i)->TagSize + parent->Subs->at(i)->Length + parent->Subs->at(i)->LengthSize); + } - if(sublen < parent->Length) - { - shared_ptr<TLVNode> subnode = TLV_Parse_One(parent->Value+sublen,parent->Length-sublen); - parent->Sub[parent->SubCount++] = subnode; - return subnode->MoreFlag; - } - else - { - return 0; + if (sublen < parent->Value->size()) + { + shared_ptr<TLVNode> subnode = TLV_Parse_One(parent->Value->data() + sublen, parent->Value->size() - sublen); + parent->Subs->push_back(subnode); + return subnode->MoreFlag; + } + else + { + return 0; + } } -} -/* Recursive function to parse all nodes starting from a root parent node */ -void TLVParser::TLV_Parse_Sub(shared_ptr<TLVNode> parent) -{ - int i; - if(parent->SubFlag != 0) + /* Recursive function to parse all nodes starting from a root parent node */ + void TLVParser::TLV_Parse_Sub(shared_ptr<TLVNode> parent) { - //Parse all sub nodes. - while(TLV_Parse_SubNodes(parent) != 0); - - for(i=0;i<parent->SubCount;i++) + size_t i; + if (parent->SubFlag != 0) { - if(parent->Sub[i]->SubFlag != 0) + // Parse all sub nodes. + while (TLV_Parse_SubNodes(parent) != 0); + + for (i = 0; i < parent->Subs->size(); i++) { - TLV_Parse_Sub(parent->Sub[i]); + if (parent->Subs->at(i)->SubFlag != 0) + { + TLV_Parse_Sub(parent->Subs->at(i)); + } } } } - return; -} -/* Parsing TLV from a buffer and constructing TLV structure */ -shared_ptr<TLVNode> TLVParser::TLV_Parse(unsigned char* buf,int size) -{ - shared_ptr<TLVNode> node = TLV_Parse_One(buf,size); - TLV_Parse_Sub(node); - - return node; -} - -/* Finding a TLV node with a particular tag */ -shared_ptr<TLVNode> TLVParser::TLV_Find(shared_ptr<TLVNode> node,uint16_t tag){ - int i; - shared_ptr<TLVNode> tmpnode; - if(node->Tag == tag) + /* Parsing TLV from a buffer and constructing TLV structure */ + shared_ptr<TLVNode> TLVParser::TLV_Parse(byte* buf, size_t size) { + shared_ptr<TLVNode> node = TLV_Parse_One(buf, size); + TLV_Parse_Sub(node); + return node; } - for(i=0;i<node->SubCount;i++) + + /* Finding a TLV node with a particular tag */ + shared_ptr<TLVNode> TLVParser::TLV_Find(shared_ptr<TLVNode> node, uint16 tag) { - tmpnode = NULL; - tmpnode = TLV_Find(node->Sub[i],tag); - if(tmpnode != NULL){ - return tmpnode; + size_t i = 0; + shared_ptr<TLVNode> tmpnode = NULL; + if (node->Tag == tag) + { + return node; } - } - if(node->Next) - { - tmpnode = NULL; - tmpnode = TLV_Find(node->Next,tag); - if(tmpnode != NULL){ - return tmpnode; + for (i = 0; i < node->Subs->size(); i++) + { + tmpnode = TLV_Find(node->Subs->at(i),tag); + if (tmpnode != NULL) + { + return tmpnode; + } } + return NULL; } - - return nullptr; -} - +}
\ No newline at end of file diff --git a/src/Common/TLVParser.h b/src/Common/TLVParser.h index e25e429f..b989ca41 100644 --- a/src/Common/TLVParser.h +++ b/src/Common/TLVParser.h @@ -1,80 +1,80 @@ -// -// Created by bshp on 1/20/23. -// - -#ifndef ICC_EXTRACTOR_TLVPARSER_H -#define ICC_EXTRACTOR_TLVPARSER_H -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <algorithm> -#include <string> -#include <memory> -#include "iostream" -#include "Tcdefs.h" -using namespace std; -struct TLVNode{ - uint16_t Tag; /* T */ - uint16_t Length; /* L */ - unsigned char* Value; /* V */ - unsigned char TagSize; - unsigned char LengthSize; - uint16_t MoreFlag; /* Used In Sub */ - uint16_t SubFlag; /* Does it have sub-nodes? */ - uint16_t SubCount; - shared_ptr<TLVNode> Sub[256]; - shared_ptr<TLVNode> Next; - - ~TLVNode() { - burn(Value, Length); - delete Value; - } -}; +#ifndef TC_HEADER_Common_TLVPARSER +#define TC_HEADER_Common_TLVPARSER -class TLVParser{ -private : +#include "Platform/PlatformBase.h" +#include "Tcdefs.h" - /* TLV node structure creation */ - static shared_ptr<TLVNode> TLV_CreateNode(); +namespace VeraCrypt +{ + struct TLVNode + { + uint16 Tag; /* T */ + uint16 Length; /* L */ + shared_ptr<vector<byte>> Value; /* V */ + byte TagSize; + byte LengthSize; + uint16 MoreFlag; /* Used In Sub */ + uint16 SubFlag; /* Does it have sub-nodes? */ + shared_ptr<vector<shared_ptr<TLVNode>>> Subs; + + TLVNode() : Tag(0), Length(0), TagSize(0), LengthSize(0), MoreFlag(0), SubFlag(0) + { + Value = make_shared<vector<byte>>(); + Subs = make_shared<vector<shared_ptr<TLVNode>>>(); + } + + ~TLVNode() + { + burn(Value->data(), Value->size()); + } + }; + + class TLVParser + { + private : - /* Check if the bit is correct */ - static uint16_t CheckBit(unsigned char value, int bit); + /* TLV node structure creation */ + static shared_ptr<TLVNode> TLV_CreateNode(); - /* Parsing one TLV node */ - static shared_ptr<TLVNode> TLV_Parse_One(unsigned char* buf,int size); + /* Check if the bit is correct */ + static uint16 CheckBit(byte value, int bit); - /* Parsing all TLV nodes */ - static int TLV_Parse_SubNodes(shared_ptr<TLVNode> parent); + /* Parsing one TLV node */ + static shared_ptr<TLVNode> TLV_Parse_One(byte* buf, size_t size); - /* Parsing all sub-nodes (in width not in depth) of a given parent node */ - static int TLV_Parse_All(shared_ptr<TLVNode> parent); + /* Parsing all TLV nodes */ + static int TLV_Parse_SubNodes(shared_ptr<TLVNode> parent); - /* Recursive function to parse all nodes starting from a root parent node */ - static void TLV_Parse_Sub(shared_ptr<TLVNode> parent); + /* Parsing all sub-nodes (in width not in depth) of a given parent node */ + static int TLV_Parse_All(shared_ptr<TLVNode> parent); -public: + /* Recursive function to parse all nodes starting from a root parent node */ + static void TLV_Parse_Sub(shared_ptr<TLVNode> parent); - /* Parsing TLV from a buffer and constructing TLV structure */ - static shared_ptr<TLVNode> TLV_Parse(unsigned char* buf,int size); + public: - /* Finding a TLV node with a particular tag */ - static shared_ptr<TLVNode> TLV_Find(shared_ptr<TLVNode> node,uint16_t tag); -}; + /* Parsing TLV from a buffer and constructing TLV structure */ + static shared_ptr<TLVNode> TLV_Parse(byte* buf, size_t size); -/* The definition of the exception class related to the TLV parsing */ -class TLVException -{ -public: - TLVException(std::string errormessage): m_errormessage(errormessage){} + /* Finding a TLV node with a particular tag */ + static shared_ptr<TLVNode> TLV_Find(shared_ptr<TLVNode> node, uint16 tag); + }; - /* Get the error message */ - inline std::string ErrorMessage() const + /* The definition of the exception class related to the TLV parsing */ + class TLVException { - return m_errormessage; - } + public: + TLVException(std::string errormessage): m_errormessage(errormessage){} + + /* Get the error message */ + inline std::string ErrorMessage() const + { + return m_errormessage; + } -protected: - std::string m_errormessage; -}; + protected: + std::string m_errormessage; + }; +} -#endif //ICC_EXTRACTOR_TLVPARSER_H +#endif //TC_HEADER_Common_TLVPARSER diff --git a/src/Common/Token.cpp b/src/Common/Token.cpp index 5da677de..17fce78b 100644 --- a/src/Common/Token.cpp +++ b/src/Common/Token.cpp @@ -17,62 +17,94 @@ #include "SecurityToken.h" #include "EMVToken.h" +#include "PCSCException.h" #include "iostream" using namespace std; namespace VeraCrypt { - vector<shared_ptr<TokenKeyfile>> Token::GetAvailableKeyfiles(bool EMVOption) { + vector<shared_ptr<TokenKeyfile>> Token::GetAvailableKeyfiles(bool isEMVSupportEnabled) + { vector<shared_ptr<TokenKeyfile>> availableKeyfiles; bool securityTokenLibraryInitialized = true; + bool scardLibraryInitialized = true; - try{ - foreach (SecurityTokenKeyfile k, SecurityToken::GetAvailableKeyfiles()) { + try + { + foreach (SecurityTokenKeyfile k, SecurityToken::GetAvailableKeyfiles()) + { availableKeyfiles.push_back(shared_ptr<TokenKeyfile>(new SecurityTokenKeyfile(k))); } - } catch (SecurityTokenLibraryNotInitialized){ + } + catch (SecurityTokenLibraryNotInitialized&) + { securityTokenLibraryInitialized = false; } + + if (isEMVSupportEnabled) + { + try + { + foreach (EMVTokenKeyfile k, EMVToken::GetAvailableKeyfiles()) + { + availableKeyfiles.push_back(shared_ptr<TokenKeyfile>(new EMVTokenKeyfile(k))); + } + } + catch (ScardLibraryInitializationFailed&) + { + scardLibraryInitialized = false; + } + } - if(EMVOption){ - foreach (EMVTokenKeyfile k, EMVToken::GetAvailableKeyfiles()) { - availableKeyfiles.push_back(shared_ptr<TokenKeyfile>(new EMVTokenKeyfile(k))); - } - } - - if(availableKeyfiles.size() == 0 && ! securityTokenLibraryInitialized){ - throw SecurityTokenLibraryNotInitialized(); + if (availableKeyfiles.size() == 0) + { + if (!securityTokenLibraryInitialized) + { + throw SecurityTokenLibraryNotInitialized(); + } + else if (!scardLibraryInitialized) + { + throw ScardLibraryInitializationFailed(); + } } return availableKeyfiles; } - bool Token::IsKeyfilePathValid(const wstring& tokenKeyfilePath, bool EMVOption) + bool Token::IsKeyfilePathValid(const wstring& tokenKeyfilePath, bool isEMVSupportEnabled) { - if(EMVOption){ - return SecurityToken::IsKeyfilePathValid(tokenKeyfilePath) || EMVToken::IsKeyfilePathValid(tokenKeyfilePath); - } + if (isEMVSupportEnabled) + { + return SecurityToken::IsKeyfilePathValid(tokenKeyfilePath) || EMVToken::IsKeyfilePathValid(tokenKeyfilePath); + } return SecurityToken::IsKeyfilePathValid(tokenKeyfilePath); } list <shared_ptr<TokenInfo>> Token::GetAvailableTokens() { list <shared_ptr<TokenInfo>> availableTokens; - foreach(SecurityTokenInfo securityToken, SecurityToken::GetAvailableTokens()){ + + foreach(SecurityTokenInfo securityToken, SecurityToken::GetAvailableTokens()) + { availableTokens.push_back(shared_ptr<TokenInfo>(new SecurityTokenInfo(std::move(securityToken)))); } return availableTokens ; } - shared_ptr<TokenKeyfile> Token::getTokenKeyfile(const TokenKeyfilePath path){ + shared_ptr<TokenKeyfile> Token::getTokenKeyfile(const TokenKeyfilePath& path) + { shared_ptr<TokenKeyfile> tokenKeyfile; - if(SecurityToken::IsKeyfilePathValid(path)){ + if (SecurityToken::IsKeyfilePathValid(path)) + { tokenKeyfile = shared_ptr<TokenKeyfile>(new SecurityTokenKeyfile(path)); - } else { - if(EMVToken::IsKeyfilePathValid(path)){ + } + else + { + if (EMVToken::IsKeyfilePathValid(path)) + { tokenKeyfile = shared_ptr<TokenKeyfile>(new EMVTokenKeyfile(path)); } } diff --git a/src/Common/Token.h b/src/Common/Token.h index 28c6a489..c1dd8ac4 100644 --- a/src/Common/Token.h +++ b/src/Common/Token.h @@ -15,42 +15,46 @@ #define UNAVAILABLE_SLOT ~0UL -namespace VeraCrypt { - - struct TokenKeyfilePath { +namespace VeraCrypt +{ + struct TokenKeyfilePath + { + virtual ~TokenKeyfilePath() {}; TokenKeyfilePath(const wstring& path): Path(path) { } operator wstring () const { return Path; } - wstring Path; //Complete path - + wstring Path; // Complete path }; - struct TokenInfo { - TokenInfo() {} + + struct TokenInfo + { + TokenInfo(): SlotId(0), Label(L"") {} virtual ~TokenInfo() {} - virtual BOOL isEditable() const=0; + virtual BOOL isEditable() const = 0; unsigned long int SlotId; - wstring Label; //Card name + wstring Label; // Card name }; - struct TokenKeyfile { + struct TokenKeyfile + { + virtual ~TokenKeyfile() {} virtual operator TokenKeyfilePath () const = 0; virtual void GetKeyfileData(vector <byte>& keyfileData) const = 0; - string IdUtf8; // Was used in SecurityToken to compare with the file name from a PKCS11 card, remove from token ? shared_ptr<TokenInfo> Token; wstring Id; }; - class Token { + class Token + { public: - static vector<shared_ptr<TokenKeyfile>> GetAvailableKeyfiles(bool EMVOption); - static bool IsKeyfilePathValid(const wstring& tokenKeyfilePath, bool EMVOption); - static list <shared_ptr<TokenInfo>> GetAvailableTokens(); // List available token to write - static shared_ptr<TokenKeyfile> getTokenKeyfile(const TokenKeyfilePath path); + static vector<shared_ptr<TokenKeyfile>> GetAvailableKeyfiles(bool isEMVSupportEnabled); + static bool IsKeyfilePathValid(const wstring& tokenKeyfilePath, bool isEMVSupportEnabled); + static list <shared_ptr<TokenInfo>> GetAvailableTokens(); + static shared_ptr<TokenKeyfile> getTokenKeyfile(const TokenKeyfilePath& path); }; - }; diff --git a/src/Common/reader.h b/src/Common/reader.h new file mode 100644 index 00000000..19fbba87 --- /dev/null +++ b/src/Common/reader.h @@ -0,0 +1,271 @@ +/* + * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ ) + * + * Copyright (C) 1999-2005 + * David Corcoran <corcoran@musclecard.com> + * Copyright (C) 2005-2009 + * Ludovic Rousseau <ludovic.rousseau@free.fr> + * +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief This keeps a list of defines shared between the driver and the application + */ + +#ifndef __reader_h__ +#define __reader_h__ + +/* + * Tags for requesting card and reader attributes + */ + +#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag))) + +#define SCARD_CLASS_VENDOR_INFO 1 /**< Vendor information definitions */ +#define SCARD_CLASS_COMMUNICATIONS 2 /**< Communication definitions */ +#define SCARD_CLASS_PROTOCOL 3 /**< Protocol definitions */ +#define SCARD_CLASS_POWER_MGMT 4 /**< Power Management definitions */ +#define SCARD_CLASS_SECURITY 5 /**< Security Assurance definitions */ +#define SCARD_CLASS_MECHANICAL 6 /**< Mechanical characteristic definitions */ +#define SCARD_CLASS_VENDOR_DEFINED 7 /**< Vendor specific definitions */ +#define SCARD_CLASS_IFD_PROTOCOL 8 /**< Interface Device Protocol options */ +#define SCARD_CLASS_ICC_STATE 9 /**< ICC State specific definitions */ +#define SCARD_CLASS_SYSTEM 0x7fff /**< System-specific definitions */ + +#define SCARD_ATTR_VENDOR_NAME SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0100) /**< Vendor name. */ +#define SCARD_ATTR_VENDOR_IFD_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0101) /**< Vendor-supplied interface device type (model designation of reader). */ +#define SCARD_ATTR_VENDOR_IFD_VERSION SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0102) /**< Vendor-supplied interface device version (DWORD in the form 0xMMmmbbbb where MM = major version, mm = minor version, and bbbb = build number). */ +#define SCARD_ATTR_VENDOR_IFD_SERIAL_NO SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0103) /**< Vendor-supplied interface device serial number. */ +#define SCARD_ATTR_CHANNEL_ID SCARD_ATTR_VALUE(SCARD_CLASS_COMMUNICATIONS, 0x0110) /**< DWORD encoded as 0xDDDDCCCC, where DDDD = data channel type and CCCC = channel number */ +#define SCARD_ATTR_ASYNC_PROTOCOL_TYPES SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0120) /**< FIXME */ +#define SCARD_ATTR_DEFAULT_CLK SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0121) /**< Default clock rate, in kHz. */ +#define SCARD_ATTR_MAX_CLK SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0122) /**< Maximum clock rate, in kHz. */ +#define SCARD_ATTR_DEFAULT_DATA_RATE SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0123) /**< Default data rate, in bps. */ +#define SCARD_ATTR_MAX_DATA_RATE SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0124) /**< Maximum data rate, in bps. */ +#define SCARD_ATTR_MAX_IFSD SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0125) /**< Maximum bytes for information file size device. */ +#define SCARD_ATTR_SYNC_PROTOCOL_TYPES SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0126) /**< FIXME */ +#define SCARD_ATTR_POWER_MGMT_SUPPORT SCARD_ATTR_VALUE(SCARD_CLASS_POWER_MGMT, 0x0131) /**< Zero if device does not support power down while smart card is inserted. Nonzero otherwise. */ +#define SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE SCARD_ATTR_VALUE(SCARD_CLASS_SECURITY, 0x0140) /**< FIXME */ +#define SCARD_ATTR_USER_AUTH_INPUT_DEVICE SCARD_ATTR_VALUE(SCARD_CLASS_SECURITY, 0x0142) /**< FIXME */ +#define SCARD_ATTR_CHARACTERISTICS SCARD_ATTR_VALUE(SCARD_CLASS_MECHANICAL, 0x0150) /**< DWORD indicating which mechanical characteristics are supported. If zero, no special characteristics are supported. Note that multiple bits can be set */ + +#define SCARD_ATTR_CURRENT_PROTOCOL_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0201) /**< FIXME */ +#define SCARD_ATTR_CURRENT_CLK SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0202) /**< Current clock rate, in kHz. */ +#define SCARD_ATTR_CURRENT_F SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0203) /**< Clock conversion factor. */ +#define SCARD_ATTR_CURRENT_D SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0204) /**< Bit rate conversion factor. */ +#define SCARD_ATTR_CURRENT_N SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0205) /**< Current guard time. */ +#define SCARD_ATTR_CURRENT_W SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0206) /**< Current work waiting time. */ +#define SCARD_ATTR_CURRENT_IFSC SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0207) /**< Current byte size for information field size card. */ +#define SCARD_ATTR_CURRENT_IFSD SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0208) /**< Current byte size for information field size device. */ +#define SCARD_ATTR_CURRENT_BWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0209) /**< Current block waiting time. */ +#define SCARD_ATTR_CURRENT_CWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020a) /**< Current character waiting time. */ +#define SCARD_ATTR_CURRENT_EBC_ENCODING SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020b) /**< Current error block control encoding. */ +#define SCARD_ATTR_EXTENDED_BWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020c) /**< FIXME */ + +#define SCARD_ATTR_ICC_PRESENCE SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0300) /**< Single byte indicating smart card presence */ +#define SCARD_ATTR_ICC_INTERFACE_STATUS SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0301) /**< Single byte. Zero if smart card electrical contact is not active; nonzero if contact is active. */ +#define SCARD_ATTR_CURRENT_IO_STATE SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0302) /**< FIXME */ +#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) /**< Answer to reset (ATR) string. */ +#define SCARD_ATTR_ICC_TYPE_PER_ATR SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0304) /**< Single byte indicating smart card type */ + +#define SCARD_ATTR_ESC_RESET SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA000) /**< FIXME */ +#define SCARD_ATTR_ESC_CANCEL SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA003) /**< FIXME */ +#define SCARD_ATTR_ESC_AUTHREQUEST SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA005) /**< FIXME */ +#define SCARD_ATTR_MAXINPUT SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA007) /**< FIXME */ + +#define SCARD_ATTR_DEVICE_UNIT SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0001) /**< Instance of this vendor's reader attached to the computer. The first instance will be device unit 0, the next will be unit 1 (if it is the same brand of reader) and so on. Two different brands of readers will both have zero for this value. */ +#define SCARD_ATTR_DEVICE_IN_USE SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0002) /**< Reserved for future use. */ +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0003) +#define SCARD_ATTR_DEVICE_SYSTEM_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0004) +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0005) +#define SCARD_ATTR_DEVICE_SYSTEM_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0006) +#define SCARD_ATTR_SUPRESS_T1_IFS_REQUEST SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0007) /**< FIXME */ + +#ifdef UNICODE +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_W /**< Reader's display name. */ +#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_W /**< Reader's system name. */ +#else +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_A /**< Reader's display name. */ +#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_A /**< Reader's system name. */ +#endif + +/** + * Provide source compatibility on different platforms + */ +#define SCARD_CTL_CODE(code) (0x42000000 + (code)) + +/** + * PC/SC part 10 v2.02.07 March 2010 reader tags + */ +#define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400) + +#define FEATURE_VERIFY_PIN_START 0x01 +#define FEATURE_VERIFY_PIN_FINISH 0x02 +#define FEATURE_MODIFY_PIN_START 0x03 +#define FEATURE_MODIFY_PIN_FINISH 0x04 +#define FEATURE_GET_KEY_PRESSED 0x05 +#define FEATURE_VERIFY_PIN_DIRECT 0x06 /**< Verify PIN */ +#define FEATURE_MODIFY_PIN_DIRECT 0x07 /**< Modify PIN */ +#define FEATURE_MCT_READER_DIRECT 0x08 +#define FEATURE_MCT_UNIVERSAL 0x09 +#define FEATURE_IFD_PIN_PROPERTIES 0x0A /**< retrieve properties of the IFD regarding PIN handling */ +#define FEATURE_ABORT 0x0B +#define FEATURE_SET_SPE_MESSAGE 0x0C +#define FEATURE_VERIFY_PIN_DIRECT_APP_ID 0x0D +#define FEATURE_MODIFY_PIN_DIRECT_APP_ID 0x0E +#define FEATURE_WRITE_DISPLAY 0x0F +#define FEATURE_GET_KEY 0x10 +#define FEATURE_IFD_DISPLAY_PROPERTIES 0x11 +#define FEATURE_GET_TLV_PROPERTIES 0x12 +#define FEATURE_CCID_ESC_COMMAND 0x13 +#define FEATURE_EXECUTE_PACE 0x20 + +/* structures used (but not defined) in PC/SC Part 10: + * "IFDs with Secure Pin Entry Capabilities" */ + +#include <inttypes.h> + +/* Set structure elements alignment on bytes + * http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html */ +#if defined(__APPLE__) || defined(sun) || defined(__NetBSD__) +#pragma pack(1) +#else +#pragma pack(push, 1) +#endif + +/** the structure must be 6-bytes long */ +typedef struct +{ + uint8_t tag; + uint8_t length; + uint32_t value; /**< This value is always in BIG ENDIAN format as documented in PCSC v2 part 10 ch 2.2 page 2. You can use ntohl() for example */ +} PCSC_TLV_STRUCTURE; + +/** Since CCID 1.4.1 (revision 5252) the byte order is no more important + * These macros are now deprecated and should be removed in the future */ +#define HOST_TO_CCID_16(x) (x) +#define HOST_TO_CCID_32(x) (x) + +/** structure used with \ref FEATURE_VERIFY_PIN_DIRECT */ +typedef struct +{ + uint8_t bTimerOut; /**< timeout is seconds (00 means use default timeout) */ + uint8_t bTimerOut2; /**< timeout in seconds after first key stroke */ + uint8_t bmFormatString; /**< formatting options */ + uint8_t bmPINBlockString; /**< bits 7-4 bit size of PIN length in APDU, + * bits 3-0 PIN block size in bytes after + * justification and formatting */ + uint8_t bmPINLengthFormat; /**< bits 7-5 RFU, + * bit 4 set if system units are bytes, clear if + * system units are bits, + * bits 3-0 PIN length position in system units */ + uint16_t wPINMaxExtraDigit; /**< 0xXXYY where XX is minimum PIN size in digits, + and YY is maximum PIN size in digits */ + uint8_t bEntryValidationCondition; /**< Conditions under which PIN entry should + * be considered complete */ + uint8_t bNumberMessage; /**< Number of messages to display for PIN verification */ + uint16_t wLangId; /**< Language for messages. https://docs.microsoft.com/en-us/windows/win32/intl/language-identifier-constants-and-strings */ + uint8_t bMsgIndex; /**< Message index (should be 00) */ + uint8_t bTeoPrologue[3]; /**< T=1 block prologue field to use (fill with 00) */ + uint32_t ulDataLength; /**< length of Data to be sent to the ICC */ + uint8_t abData +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; /**< Data to send to the ICC */ +} PIN_VERIFY_STRUCTURE; + +/** structure used with \ref FEATURE_MODIFY_PIN_DIRECT */ +typedef struct +{ + uint8_t bTimerOut; /**< timeout is seconds (00 means use default timeout) */ + uint8_t bTimerOut2; /**< timeout in seconds after first key stroke */ + uint8_t bmFormatString; /**< formatting options */ + uint8_t bmPINBlockString; /**< bits 7-4 bit size of PIN length in APDU, + * bits 3-0 PIN block size in bytes after + * justification and formatting */ + uint8_t bmPINLengthFormat; /**< bits 7-5 RFU, + * bit 4 set if system units are bytes, clear if + * system units are bits, + * bits 3-0 PIN length position in system units */ + uint8_t bInsertionOffsetOld; /**< Insertion position offset in bytes for + the current PIN */ + uint8_t bInsertionOffsetNew; /**< Insertion position offset in bytes for + the new PIN */ + uint16_t wPINMaxExtraDigit; + /**< 0xXXYY where XX is minimum PIN size in digits, + and YY is maximum PIN size in digits */ + uint8_t bConfirmPIN; /**< Flags governing need for confirmation of new PIN */ + uint8_t bEntryValidationCondition; /**< Conditions under which PIN entry should + * be considered complete */ + uint8_t bNumberMessage; /**< Number of messages to display for PIN verification*/ + uint16_t wLangId; /**< Language for messages. https://docs.microsoft.com/en-us/windows/win32/intl/language-identifier-constants-and-strings */ + uint8_t bMsgIndex1; /**< index of 1st prompting message */ + uint8_t bMsgIndex2; /**< index of 2d prompting message */ + uint8_t bMsgIndex3; /**< index of 3d prompting message */ + uint8_t bTeoPrologue[3]; /**< T=1 block prologue field to use (fill with 00) */ + uint32_t ulDataLength; /**< length of Data to be sent to the ICC */ + uint8_t abData +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; /**< Data to send to the ICC */ +} PIN_MODIFY_STRUCTURE; + +/** structure used with \ref FEATURE_IFD_PIN_PROPERTIES */ +typedef struct { + uint16_t wLcdLayout; /**< display characteristics */ + uint8_t bEntryValidationCondition; + uint8_t bTimeOut2; +} PIN_PROPERTIES_STRUCTURE; + +/* restore default structure elements alignment */ +#if defined(__APPLE__) || defined(sun) || defined(__NetBSD__) +#pragma pack() +#else +#pragma pack(pop) +#endif + +/* properties returned by FEATURE_GET_TLV_PROPERTIES */ +#define PCSCv2_PART10_PROPERTY_wLcdLayout 1 +#define PCSCv2_PART10_PROPERTY_bEntryValidationCondition 2 +#define PCSCv2_PART10_PROPERTY_bTimeOut2 3 +#define PCSCv2_PART10_PROPERTY_wLcdMaxCharacters 4 +#define PCSCv2_PART10_PROPERTY_wLcdMaxLines 5 +#define PCSCv2_PART10_PROPERTY_bMinPINSize 6 +#define PCSCv2_PART10_PROPERTY_bMaxPINSize 7 +#define PCSCv2_PART10_PROPERTY_sFirmwareID 8 +#define PCSCv2_PART10_PROPERTY_bPPDUSupport 9 +#define PCSCv2_PART10_PROPERTY_dwMaxAPDUDataSize 10 +#define PCSCv2_PART10_PROPERTY_wIdVendor 11 +#define PCSCv2_PART10_PROPERTY_wIdProduct 12 + +#endif + |