VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Common/SCardReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Common/SCardReader.cpp')
-rw-r--r--src/Common/SCardReader.cpp681
1 files changed, 681 insertions, 0 deletions
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);
+ }
+ }
+}
+